/*

This file is part of Sencha Touch 2

Copyright (c) 2011 Sencha Inc

Contact:  http://www.sencha.com/contact

Commercial Usage
Licensees holding valid commercial licenses may use this file in accordance with the Commercial Software License Agreement provided with the Software or, alternatively, in accordance with the terms contained in a written agreement between you and Sencha.

If you are unsure which license is appropriate for your use, please contact the sales department at http://www.sencha.com/contact.

*/


(function() {
    var global = this,
        objectPrototype = Object.prototype,
        toString = objectPrototype.toString,
        enumerables = true,
        enumerablesTest = { toString: 1 },
        emptyFn = function(){},
        i;

    if (typeof Ext === 'undefined') {
        global.Ext = {};
    }

    Ext.global = global;

    for (i in enumerablesTest) {
        enumerables = null;
    }

    if (enumerables) {
        enumerables = ['hasOwnProperty', 'valueOf', 'isPrototypeOf', 'propertyIsEnumerable',
                       'toLocaleString', 'toString', 'constructor'];
    }

    
    Ext.enumerables = enumerables;

    
    Ext.apply = function(object, config, defaults) {
        if (defaults) {
            Ext.apply(object, defaults);
        }

        if (object && config && typeof config === 'object') {
            var i, j, k;

            for (i in config) {
                object[i] = config[i];
            }

            if (enumerables) {
                for (j = enumerables.length; j--;) {
                    k = enumerables[j];
                    if (config.hasOwnProperty(k)) {
                        object[k] = config[k];
                    }
                }
            }
        }

        return object;
    };

    Ext.buildSettings = Ext.apply({
        baseCSSPrefix: 'x-',
        scopeResetCSS: false
    }, Ext.buildSettings || {});

    Ext.apply(Ext, {
        
        emptyFn: emptyFn,

        baseCSSPrefix: Ext.buildSettings.baseCSSPrefix,

        
        applyIf: function(object, config) {
            var property;

            if (object) {
                for (property in config) {
                    if (object[property] === undefined) {
                        object[property] = config[property];
                    }
                }
            }

            return object;
        },

        
        iterate: function(object, fn, scope) {
            if (Ext.isEmpty(object)) {
                return;
            }

            if (scope === undefined) {
                scope = object;
            }

            if (Ext.isIterable(object)) {
                Ext.Array.each.call(Ext.Array, object, fn, scope);
            }
            else {
                Ext.Object.each.call(Ext.Object, object, fn, scope);
            }
        }
    });

    Ext.apply(Ext, {

        
        extend: function() {
            
            var objectConstructor = objectPrototype.constructor,
                inlineOverrides = function(o) {
                for (var m in o) {
                    if (!o.hasOwnProperty(m)) {
                        continue;
                    }
                    this[m] = o[m];
                }
            };

            return function(subclass, superclass, overrides) {
                
                if (Ext.isObject(superclass)) {
                    overrides = superclass;
                    superclass = subclass;
                    subclass = overrides.constructor !== objectConstructor ? overrides.constructor : function() {
                        superclass.apply(this, arguments);
                    };
                }

                if (!superclass) {
                    Ext.Error.raise({
                        sourceClass: 'Ext',
                        sourceMethod: 'extend',
                        msg: 'Attempting to extend from a class which has not been loaded on the page.'
                    });
                }

                
                var F = function() {},
                    subclassProto, superclassProto = superclass.prototype;

                F.prototype = superclassProto;
                subclassProto = subclass.prototype = new F();
                subclassProto.constructor = subclass;
                subclass.superclass = superclassProto;

                if (superclassProto.constructor === objectConstructor) {
                    superclassProto.constructor = superclass;
                }

                subclass.override = function(overrides) {
                    Ext.override(subclass, overrides);
                };

                subclassProto.override = inlineOverrides;
                subclassProto.proto = subclassProto;

                subclass.override(overrides);
                subclass.extend = function(o) {
                    return Ext.extend(subclass, o);
                };

                return subclass;
            };
        }(),

        
        override: function(cls, overrides) {
            if (cls.$isClass) {
                return cls.override(overrides);
            }
            else {
                Ext.apply(cls.prototype, overrides);
            }
        }
    });

    
    Ext.apply(Ext, {

        
        valueFrom: function(value, defaultValue, allowBlank){
            return Ext.isEmpty(value, allowBlank) ? defaultValue : value;
        },

        
        typeOf: function(value) {
            if (value === null) {
                return 'null';
            }

            var type = typeof value;

            if (type === 'undefined' || type === 'string' || type === 'number' || type === 'boolean') {
                return type;
            }

            var typeToString = toString.call(value);

            switch(typeToString) {
                case '[object Array]':
                    return 'array';
                case '[object Date]':
                    return 'date';
                case '[object Boolean]':
                    return 'boolean';
                case '[object Number]':
                    return 'number';
                case '[object RegExp]':
                    return 'regexp';
            }

            if (type === 'function') {
                return 'function';
            }

            if (type === 'object') {
                if (value.nodeType !== undefined) {
                    if (value.nodeType === 3) {
                        return (/\S/).test(value.nodeValue) ? 'textnode' : 'whitespace';
                    }
                    else {
                        return 'element';
                    }
                }

                return 'object';
            }

            Ext.Error.raise({
                sourceClass: 'Ext',
                sourceMethod: 'typeOf',
                msg: 'Failed to determine the type of the specified value "' + value + '". This is most likely a bug.'
            });
        },

        
        isEmpty: function(value, allowEmptyString) {
            return (value === null) || (value === undefined) || (!allowEmptyString ? value === '' : false) || (Ext.isArray(value) && value.length === 0);
        },

        
        isArray: ('isArray' in Array) ? Array.isArray : function(value) {
            return toString.call(value) === '[object Array]';
        },

        
        isDate: function(value) {
            return toString.call(value) === '[object Date]';
        },

        
        isObject: (toString.call(null) === '[object Object]') ?
        function(value) {
            
            return value !== null && value !== undefined && toString.call(value) === '[object Object]' && value.ownerDocument === undefined;
        } :
        function(value) {
            return toString.call(value) === '[object Object]';
        },

        
        isSimpleObject: function(value) {
            return value instanceof Object && value.constructor === Object;
        },
        
        isPrimitive: function(value) {
            var type = typeof value;

            return type === 'string' || type === 'number' || type === 'boolean';
        },

        
        isFunction:
        
        
        (typeof document !== 'undefined' && typeof document.getElementsByTagName('body') === 'function') ? function(value) {
            return toString.call(value) === '[object Function]';
        } : function(value) {
            return typeof value === 'function';
        },

        
        isNumber: function(value) {
            return typeof value === 'number' && isFinite(value);
        },

        
        isNumeric: function(value) {
            return !isNaN(parseFloat(value)) && isFinite(value);
        },

        
        isString: function(value) {
            return typeof value === 'string';
        },

        
        isBoolean: function(value) {
            return typeof value === 'boolean';
        },

        
        isElement: function(value) {
            return value ? value.nodeType === 1 : false;
        },

        
        isTextNode: function(value) {
            return value ? value.nodeName === "#text" : false;
        },

        
        isDefined: function(value) {
            return typeof value !== 'undefined';
        },

        
        isIterable: function(value) {
            return (value && typeof value !== 'string') ? value.length !== undefined : false;
        }
    });

    Ext.apply(Ext, {

        
        clone: function(item) {
            if (item === null || item === undefined) {
                return item;
            }

            
            
            
            if (item.nodeType && item.cloneNode) {
                return item.cloneNode(true);
            }

            var type = toString.call(item);

            
            if (type === '[object Date]') {
                return new Date(item.getTime());
            }

            var i, j, k, clone, key;

            
            if (type === '[object Array]') {
                i = item.length;

                clone = [];

                while (i--) {
                    clone[i] = Ext.clone(item[i]);
                }
            }
            
            else if (type === '[object Object]' && item.constructor === Object) {
                clone = {};

                for (key in item) {
                    clone[key] = Ext.clone(item[key]);
                }

                if (enumerables) {
                    for (j = enumerables.length; j--;) {
                        k = enumerables[j];
                        clone[k] = item[k];
                    }
                }
            }

            return clone || item;
        },

        
        getUniqueGlobalNamespace: function() {
            var uniqueGlobalNamespace = this.uniqueGlobalNamespace;

            if (uniqueGlobalNamespace === undefined) {
                var i = 0;

                do {
                    uniqueGlobalNamespace = 'ExtBox' + (++i);
                } while (Ext.global[uniqueGlobalNamespace] !== undefined);

                Ext.global[uniqueGlobalNamespace] = Ext;
                this.uniqueGlobalNamespace = uniqueGlobalNamespace;
            }

            return uniqueGlobalNamespace;
        },

        
        functionFactory: function() {
            var args = Array.prototype.slice.call(arguments),
                ln = args.length;

            if (ln > 0) {
                args[ln - 1] = 'var Ext=window.' + this.getUniqueGlobalNamespace() + ';' + args[ln - 1];
            }

            return Function.prototype.constructor.apply(Function.prototype, args);
        },

        
        globalEval: ('execScript' in global) ? function(code) {
            global.execScript(code)
        } : function(code) {
            (function(){
                eval(code);
            })();
        },

        
        Logger: {
            verbose: emptyFn,
            log: emptyFn,
            info: emptyFn,
            warn: emptyFn,
            error: function(message) {
                throw new Error(message);
            },
            deprecate: emptyFn
        }
    });

    
    Ext.type = Ext.typeOf;

})();


(function() {


var version = '4.1.0', Version;
    Ext.Version = Version = Ext.extend(Object, {

        
        constructor: function(version) {
            var parts, releaseStartIndex;

            if (version instanceof Version) {
                return version;
            }

            this.version = this.shortVersion = String(version).toLowerCase().replace(/_/g, '.').replace(/[\-+]/g, '');

            releaseStartIndex = this.version.search(/([^\d\.])/);

            if (releaseStartIndex !== -1) {
                this.release = this.version.substr(releaseStartIndex, version.length);
                this.shortVersion = this.version.substr(0, releaseStartIndex);
            }

            this.shortVersion = this.shortVersion.replace(/[^\d]/g, '');

            parts = this.version.split('.');

            this.major = parseInt(parts.shift() || 0, 10);
            this.minor = parseInt(parts.shift() || 0, 10);
            this.patch = parseInt(parts.shift() || 0, 10);
            this.build = parseInt(parts.shift() || 0, 10);

            return this;
        },

        
        toString: function() {
            return this.version;
        },

        
        valueOf: function() {
            return this.version;
        },

        
        getMajor: function() {
            return this.major || 0;
        },

        
        getMinor: function() {
            return this.minor || 0;
        },

        
        getPatch: function() {
            return this.patch || 0;
        },

        
        getBuild: function() {
            return this.build || 0;
        },

        
        getRelease: function() {
            return this.release || '';
        },

        
        isGreaterThan: function(target) {
            return Version.compare(this.version, target) === 1;
        },

        
        isGreaterThanOrEqual: function(target) {
            return Version.compare(this.version, target) >= 0;
        },

        
        isLessThan: function(target) {
            return Version.compare(this.version, target) === -1;
        },

        
        isLessThanOrEqual: function(target) {
            return Version.compare(this.version, target) <= 0;
        },

        
        equals: function(target) {
            return Version.compare(this.version, target) === 0;
        },

        
        match: function(target) {
            target = String(target);
            return this.version.substr(0, target.length) === target;
        },

        
        toArray: function() {
            return [this.getMajor(), this.getMinor(), this.getPatch(), this.getBuild(), this.getRelease()];
        },

        
        getShortVersion: function() {
            return this.shortVersion;
        },

        
        gt: function() {
            return this.isGreaterThan.apply(this, arguments);
        },

        
        lt: function() {
            return this.isLessThan.apply(this, arguments);
        },

        
        gtEq: function() {
            return this.isGreaterThanOrEqual.apply(this, arguments);
        },

        
        ltEq: function() {
            return this.isLessThanOrEqual.apply(this, arguments);
        }
    });

    Ext.apply(Version, {
        
        releaseValueMap: {
            'dev': -6,
            'alpha': -5,
            'a': -5,
            'beta': -4,
            'b': -4,
            'rc': -3,
            '#': -2,
            'p': -1,
            'pl': -1
        },

        
        getComponentValue: function(value) {
            return !value ? 0 : (isNaN(value) ? this.releaseValueMap[value] || value : parseInt(value, 10));
        },

        
        compare: function(current, target) {
            var currentValue, targetValue, i;

            current = new Version(current).toArray();
            target = new Version(target).toArray();

            for (i = 0; i < Math.max(current.length, target.length); i++) {
                currentValue = this.getComponentValue(current[i]);
                targetValue = this.getComponentValue(target[i]);

                if (currentValue < targetValue) {
                    return -1;
                } else if (currentValue > targetValue) {
                    return 1;
                }
            }

            return 0;
        }
    });

    Ext.apply(Ext, {
        
        versions: {},

        
        lastRegisteredVersion: null,

        
        setVersion: function(packageName, version) {
            Ext.versions[packageName] = new Version(version);
            Ext.lastRegisteredVersion = Ext.versions[packageName];

            return this;
        },

        
        getVersion: function(packageName) {
            if (packageName === undefined) {
                return Ext.lastRegisteredVersion;
            }

            return Ext.versions[packageName];
        },

        
        deprecate: function(packageName, since, closure, scope) {
            if (Version.compare(Ext.getVersion(packageName), since) < 1) {
                closure.call(scope);
            }
        }
    }); 

    Ext.setVersion('core', version);

})();



Ext.String = {
    trimRegex: /^[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u2028\u2029\u202f\u205f\u3000]+|[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u2028\u2029\u202f\u205f\u3000]+$/g,
    escapeRe: /('|\\)/g,
    formatRe: /\{(\d+)\}/g,
    escapeRegexRe: /([-.*+?^${}()|[\]\/\\])/g,

    /**
     * Convert certain characters (&, <, >, and ") to their HTML character equivalents for literal display in web pages.
     * @param {String} value The string to encode
     * @return {String} The encoded text
     * @method
     */
    htmlEncode: (function() {
        var entities = {
            '&': '&amp;',
            '>': '&gt;',
            '<': '&lt;',
            '"': '&quot;'
        }, keys = [], p, regex;

        for (p in entities) {
            keys.push(p);
        }

        regex = new RegExp('(' + keys.join('|') + ')', 'g');

        return function(value) {
            return (!value) ? value : String(value).replace(regex, function(match, capture) {
                return entities[capture];
            });
        };
    })(),

    /**
     * Convert certain characters (&, <, >, and ") from their HTML character equivalents.
     * @param {String} value The string to decode
     * @return {String} The decoded text
     * @method
     */
    htmlDecode: (function() {
        var entities = {
            '&amp;': '&',
            '&gt;': '>',
            '&lt;': '<',
            '&quot;': '"'
        }, keys = [], p, regex;

        for (p in entities) {
            keys.push(p);
        }

        regex = new RegExp('(' + keys.join('|') + '|&#[0-9]{1,5};' + ')', 'g');

        return function(value) {
            return (!value) ? value : String(value).replace(regex, function(match, capture) {
                if (capture in entities) {
                    return entities[capture];
                } else {
                    return String.fromCharCode(parseInt(capture.substr(2), 10));
                }
            });
        };
    })(),

    
    urlAppend : function(url, string) {
        if (!Ext.isEmpty(string)) {
            return url + (url.indexOf('?') === -1 ? '?' : '&') + string;
        }

        return url;
    },

    
    trim: function(string) {
        return string.replace(Ext.String.trimRegex, "");
    },

    
    capitalize: function(string) {
        return string.charAt(0).toUpperCase() + string.substr(1);
    },

    
    ellipsis: function(value, len, word) {
        if (value && value.length > len) {
            if (word) {
                var vs = value.substr(0, len - 2),
                index = Math.max(vs.lastIndexOf(' '), vs.lastIndexOf('.'), vs.lastIndexOf('!'), vs.lastIndexOf('?'));
                if (index !== -1 && index >= (len - 15)) {
                    return vs.substr(0, index) + "...";
                }
            }
            return value.substr(0, len - 3) + "...";
        }
        return value;
    },

    
    escapeRegex: function(string) {
        return string.replace(Ext.String.escapeRegexRe, "\\$1");
    },

    
    escape: function(string) {
        return string.replace(Ext.String.escapeRe, "\\$1");
    },

    
    toggle: function(string, value, other) {
        return string === value ? other : value;
    },

    
    leftPad: function(string, size, character) {
        var result = String(string);
        character = character || " ";
        while (result.length < size) {
            result = character + result;
        }
        return result;
    },

    
    format: function(format) {
        var args = Ext.Array.toArray(arguments, 1);
        return format.replace(Ext.String.formatRe, function(m, i) {
            return args[i];
        });
    },

    
    repeat: function(pattern, count, sep) {
        for (var buf = [], i = count; i--; ) {
            buf.push(pattern);
        }
        return buf.join(sep || '');
    }
};


Ext.htmlEncode = Ext.String.htmlEncode;



Ext.htmlDecode = Ext.String.htmlDecode;


Ext.urlAppend = Ext.String.urlAppend;


(function() {

    var arrayPrototype = Array.prototype,
        slice = arrayPrototype.slice,
        supportsSplice = function () {
            var array = [],
                lengthBefore,
                j = 20;

            if (!array.splice) {
                return false;
            }

            
            

            while (j--) {
                array.push("A");
            }

            array.splice(15, 0, "F", "F", "F", "F", "F","F","F","F","F","F","F","F","F","F","F","F","F","F","F","F","F");

            lengthBefore = array.length; 
            array.splice(13, 0, "XXX"); 

            if (lengthBefore+1 != array.length) {
                return false;
            }
            

            return true;
        }(),
        supportsForEach = 'forEach' in arrayPrototype,
        supportsMap = 'map' in arrayPrototype,
        supportsIndexOf = 'indexOf' in arrayPrototype,
        supportsEvery = 'every' in arrayPrototype,
        supportsSome = 'some' in arrayPrototype,
        supportsFilter = 'filter' in arrayPrototype,
        supportsSort = function() {
            var a = [1,2,3,4,5].sort(function(){ return 0; });
            return a[0] === 1 && a[1] === 2 && a[2] === 3 && a[3] === 4 && a[4] === 5;
        }(),
        supportsSliceOnNodeList = true,
        ExtArray;

    try {
        
        if (typeof document !== 'undefined') {
            slice.call(document.getElementsByTagName('body'));
        }
    } catch (e) {
        supportsSliceOnNodeList = false;
    }

    function fixArrayIndex (array, index) {
        return (index < 0) ? Math.max(0, array.length + index)
                           : Math.min(array.length, index);
    }

    
    function replaceSim (array, index, removeCount, insert) {
        var add = insert ? insert.length : 0,
            length = array.length,
            pos = fixArrayIndex(array, index);

        
        if (pos === length) {
            if (add) {
                array.push.apply(array, insert);
            }
        } else {
            var remove = Math.min(removeCount, length - pos),
                tailOldPos = pos + remove,
                tailNewPos = tailOldPos + add - remove,
                tailCount = length - tailOldPos,
                lengthAfterRemove = length - remove,
                i;

            if (tailNewPos < tailOldPos) { 
                for (i = 0; i < tailCount; ++i) {
                    array[tailNewPos+i] = array[tailOldPos+i];
                }
            } else if (tailNewPos > tailOldPos) { 
                for (i = tailCount; i--; ) {
                    array[tailNewPos+i] = array[tailOldPos+i];
                }
            } 

            if (add && pos === lengthAfterRemove) {
                array.length = lengthAfterRemove; 
                array.push.apply(array, insert);
            } else {
                array.length = lengthAfterRemove + add; 
                for (i = 0; i < add; ++i) {
                    array[pos+i] = insert[i];
                }
            }
        }

        return array;
    }

    function replaceNative (array, index, removeCount, insert) {
        if (insert && insert.length) {
            if (index < array.length) {
                array.splice.apply(array, [index, removeCount].concat(insert));
            } else {
                array.push.apply(array, insert);
            }
        } else {
            array.splice(index, removeCount);
        }
        return array;
    }

    function eraseSim (array, index, removeCount) {
        return replaceSim(array, index, removeCount);
    }

    function eraseNative (array, index, removeCount) {
        array.splice(index, removeCount);
        return array;
    }

    function spliceSim (array, index, removeCount) {
        var pos = fixArrayIndex(array, index),
            removed = array.slice(index, fixArrayIndex(array, pos+removeCount));

        if (arguments.length < 4) {
            replaceSim(array, pos, removeCount);
        } else {
            replaceSim(array, pos, removeCount, slice.call(arguments, 3));
        }

        return removed;
    }

    function spliceNative (array) {
        return array.splice.apply(array, slice.call(arguments, 1));
    }

    var erase = supportsSplice ? eraseNative : eraseSim,
        replace = supportsSplice ? replaceNative : replaceSim,
        splice = supportsSplice ? spliceNative : spliceSim;

    

    ExtArray = Ext.Array = {
        
        each: function(array, fn, scope, reverse) {
            array = ExtArray.from(array);

            var i,
                ln = array.length;

            if (reverse !== true) {
                for (i = 0; i < ln; i++) {
                    if (fn.call(scope || array[i], array[i], i, array) === false) {
                        return i;
                    }
                }
            }
            else {
                for (i = ln - 1; i > -1; i--) {
                    if (fn.call(scope || array[i], array[i], i, array) === false) {
                        return i;
                    }
                }
            }

            return true;
        },

        
        forEach: function(array, fn, scope) {
            if (supportsForEach) {
                return array.forEach(fn, scope);
            }

            var i = 0,
                ln = array.length;

            for (; i < ln; i++) {
                fn.call(scope, array[i], i, array);
            }
        },

        
        indexOf: function(array, item, from) {
            if (supportsIndexOf) {
                return array.indexOf(item, from);
            }

            var i, length = array.length;

            for (i = (from < 0) ? Math.max(0, length + from) : from || 0; i < length; i++) {
                if (array[i] === item) {
                    return i;
                }
            }

            return -1;
        },

        
        contains: function(array, item) {
            if (supportsIndexOf) {
                return array.indexOf(item) !== -1;
            }

            var i, ln;

            for (i = 0, ln = array.length; i < ln; i++) {
                if (array[i] === item) {
                    return true;
                }
            }

            return false;
        },

        
        toArray: function(iterable, start, end){
            if (!iterable || !iterable.length) {
                return [];
            }

            if (typeof iterable === 'string') {
                iterable = iterable.split('');
            }

            if (supportsSliceOnNodeList) {
                return slice.call(iterable, start || 0, end || iterable.length);
            }

            var array = [],
                i;

            start = start || 0;
            end = end ? ((end < 0) ? iterable.length + end : end) : iterable.length;

            for (i = start; i < end; i++) {
                array.push(iterable[i]);
            }

            return array;
        },

        
        pluck: function(array, propertyName) {
            var ret = [],
                i, ln, item;

            for (i = 0, ln = array.length; i < ln; i++) {
                item = array[i];

                ret.push(item[propertyName]);
            }

            return ret;
        },

        
        map: function(array, fn, scope) {
            if (supportsMap) {
                return array.map(fn, scope);
            }

            var results = [],
                i = 0,
                len = array.length;

            for (; i < len; i++) {
                results[i] = fn.call(scope, array[i], i, array);
            }

            return results;
        },

        
        every: function(array, fn, scope) {
            if (!fn) {
                Ext.Error.raise('Ext.Array.every must have a callback function passed as second argument.');
            }
            if (supportsEvery) {
                return array.every(fn, scope);
            }

            var i = 0,
                ln = array.length;

            for (; i < ln; ++i) {
                if (!fn.call(scope, array[i], i, array)) {
                    return false;
                }
            }

            return true;
        },

        
        some: function(array, fn, scope) {
            if (!fn) {
                Ext.Error.raise('Ext.Array.some must have a callback function passed as second argument.');
            }
            if (supportsSome) {
                return array.some(fn, scope);
            }

            var i = 0,
                ln = array.length;

            for (; i < ln; ++i) {
                if (fn.call(scope, array[i], i, array)) {
                    return true;
                }
            }

            return false;
        },

        
        clean: function(array) {
            var results = [],
                i = 0,
                ln = array.length,
                item;

            for (; i < ln; i++) {
                item = array[i];

                if (!Ext.isEmpty(item)) {
                    results.push(item);
                }
            }

            return results;
        },

        
        unique: function(array) {
            var clone = [],
                i = 0,
                ln = array.length,
                item;

            for (; i < ln; i++) {
                item = array[i];

                if (ExtArray.indexOf(clone, item) === -1) {
                    clone.push(item);
                }
            }

            return clone;
        },

        
        filter: function(array, fn, scope) {
            if (supportsFilter) {
                return array.filter(fn, scope);
            }

            var results = [],
                i = 0,
                ln = array.length;

            for (; i < ln; i++) {
                if (fn.call(scope, array[i], i, array)) {
                    results.push(array[i]);
                }
            }

            return results;
        },

        
        from: function(value, newReference) {
            if (value === undefined || value === null) {
                return [];
            }

            if (Ext.isArray(value)) {
                return (newReference) ? slice.call(value) : value;
            }

            if (value && value.length !== undefined && typeof value !== 'string') {
                return ExtArray.toArray(value);
            }

            return [value];
        },

        
        remove: function(array, item) {
            var index = ExtArray.indexOf(array, item);

            if (index !== -1) {
                erase(array, index, 1);
            }

            return array;
        },

        
        include: function(array, item) {
            if (!ExtArray.contains(array, item)) {
                array.push(item);
            }
        },

        
        clone: function(array) {
            return slice.call(array);
        },

        
        merge: function() {
            var args = slice.call(arguments),
                array = [],
                i, ln;

            for (i = 0, ln = args.length; i < ln; i++) {
                array = array.concat(args[i]);
            }

            return ExtArray.unique(array);
        },

        
        intersect: function() {
            var intersect = [],
                arrays = slice.call(arguments),
                i, j, k, minArray, array, x, y, ln, arraysLn, arrayLn;

            if (!arrays.length) {
                return intersect;
            }

            
            for (i = x = 0,ln = arrays.length; i < ln,array = arrays[i]; i++) {
                if (!minArray || array.length < minArray.length) {
                    minArray = array;
                    x = i;
                }
            }

            minArray = ExtArray.unique(minArray);
            erase(arrays, x, 1);

            
            
            
            for (i = 0,ln = minArray.length; i < ln,x = minArray[i]; i++) {
                var count = 0;

                for (j = 0,arraysLn = arrays.length; j < arraysLn,array = arrays[j]; j++) {
                    for (k = 0,arrayLn = array.length; k < arrayLn,y = array[k]; k++) {
                        if (x === y) {
                            count++;
                            break;
                        }
                    }
                }

                if (count === arraysLn) {
                    intersect.push(x);
                }
            }

            return intersect;
        },

        
        difference: function(arrayA, arrayB) {
            var clone = slice.call(arrayA),
                ln = clone.length,
                i, j, lnB;

            for (i = 0,lnB = arrayB.length; i < lnB; i++) {
                for (j = 0; j < ln; j++) {
                    if (clone[j] === arrayB[i]) {
                        erase(clone, j, 1);
                        j--;
                        ln--;
                    }
                }
            }

            return clone;
        },

        
        slice: function(array, begin, end) {
            return slice.call(array, begin, end);
        },

        
        sort: function(array, sortFn) {
            if (supportsSort) {
                if (sortFn) {
                    return array.sort(sortFn);
                } else {
                    return array.sort();
                }
            }

            var length = array.length,
                i = 0,
                comparison,
                j, min, tmp;

            for (; i < length; i++) {
                min = i;
                for (j = i + 1; j < length; j++) {
                    if (sortFn) {
                        comparison = sortFn(array[j], array[min]);
                        if (comparison < 0) {
                            min = j;
                        }
                    } else if (array[j] < array[min]) {
                        min = j;
                    }
                }
                if (min !== i) {
                    tmp = array[i];
                    array[i] = array[min];
                    array[min] = tmp;
                }
            }

            return array;
        },

        
        flatten: function(array) {
            var worker = [];

            function rFlatten(a) {
                var i, ln, v;

                for (i = 0, ln = a.length; i < ln; i++) {
                    v = a[i];

                    if (Ext.isArray(v)) {
                        rFlatten(v);
                    } else {
                        worker.push(v);
                    }
                }

                return worker;
            }

            return rFlatten(array);
        },

        
        min: function(array, comparisonFn) {
            var min = array[0],
                i, ln, item;

            for (i = 0, ln = array.length; i < ln; i++) {
                item = array[i];

                if (comparisonFn) {
                    if (comparisonFn(min, item) === 1) {
                        min = item;
                    }
                }
                else {
                    if (item < min) {
                        min = item;
                    }
                }
            }

            return min;
        },

        
        max: function(array, comparisonFn) {
            var max = array[0],
                i, ln, item;

            for (i = 0, ln = array.length; i < ln; i++) {
                item = array[i];

                if (comparisonFn) {
                    if (comparisonFn(max, item) === -1) {
                        max = item;
                    }
                }
                else {
                    if (item > max) {
                        max = item;
                    }
                }
            }

            return max;
        },

        
        mean: function(array) {
            return array.length > 0 ? ExtArray.sum(array) / array.length : undefined;
        },

        
        sum: function(array) {
            var sum = 0,
                i, ln, item;

            for (i = 0,ln = array.length; i < ln; i++) {
                item = array[i];

                sum += item;
            }

            return sum;
        },

        _replaceSim: replaceSim, 
        _spliceSim: spliceSim,

        
        erase: erase,

        
        insert: function (array, index, items) {
            return replace(array, index, 0, items);
        },

        
        replace: replace,

        
        splice: splice
    };

    
    Ext.each = ExtArray.each;

    
    ExtArray.union = ExtArray.merge;

    
    Ext.min = ExtArray.min;

    
    Ext.max = ExtArray.max;

    
    Ext.sum = ExtArray.sum;

    
    Ext.mean = ExtArray.mean;

    
    Ext.flatten = ExtArray.flatten;

    
    Ext.clean = ExtArray.clean;

    
    Ext.unique = ExtArray.unique;

    
    Ext.pluck = ExtArray.pluck;

    
    Ext.toArray = function() {
        return ExtArray.toArray.apply(ExtArray, arguments);
    };
})();



(function() {

var isToFixedBroken = (0.9).toFixed() !== '1';

Ext.Number = {
    
    constrain: function(number, min, max) {
        number = parseFloat(number);

        if (!isNaN(min)) {
            number = Math.max(number, min);
        }
        if (!isNaN(max)) {
            number = Math.min(number, max);
        }
        return number;
    },

    
    snap : function(value, increment, minValue, maxValue) {
        var newValue = value,
            m;

        if (!(increment && value)) {
            return value;
        }
        m = value % increment;
        if (m !== 0) {
            newValue -= m;
            if (m * 2 >= increment) {
                newValue += increment;
            } else if (m * 2 < -increment) {
                newValue -= increment;
            }
        }
        return Ext.Number.constrain(newValue, minValue,  maxValue);
    },

    
    toFixed: function(value, precision) {
        if (isToFixedBroken) {
            precision = precision || 0;
            var pow = Math.pow(10, precision);
            return (Math.round(value * pow) / pow).toFixed(precision);
        }

        return value.toFixed(precision);
    },

    
    from: function(value, defaultValue) {
        if (isFinite(value)) {
            value = parseFloat(value);
        }

        return !isNaN(value) ? value : defaultValue;
    }
};

})();


Ext.num = function() {
    return Ext.Number.from.apply(this, arguments);
};


(function() {


var TemplateClass = function(){};

var ExtObject = Ext.Object = {

    
    chain: function (object) {
        TemplateClass.prototype = object;
        var result = new TemplateClass();
        TemplateClass.prototype = null;
        return result;
    },

    
    toQueryObjects: function(name, value, recursive) {
        var self = ExtObject.toQueryObjects,
            objects = [],
            i, ln;

        if (Ext.isArray(value)) {
            for (i = 0, ln = value.length; i < ln; i++) {
                if (recursive) {
                    objects = objects.concat(self(name + '[' + i + ']', value[i], true));
                }
                else {
                    objects.push({
                        name: name,
                        value: value[i]
                    });
                }
            }
        }
        else if (Ext.isObject(value)) {
            for (i in value) {
                if (value.hasOwnProperty(i)) {
                    if (recursive) {
                        objects = objects.concat(self(name + '[' + i + ']', value[i], true));
                    }
                    else {
                        objects.push({
                            name: name,
                            value: value[i]
                        });
                    }
                }
            }
        }
        else {
            objects.push({
                name: name,
                value: value
            });
        }

        return objects;
    },

    
    toQueryString: function(object, recursive) {
        var paramObjects = [],
            params = [],
            i, j, ln, paramObject, value;

        for (i in object) {
            if (object.hasOwnProperty(i)) {
                paramObjects = paramObjects.concat(ExtObject.toQueryObjects(i, object[i], recursive));
            }
        }

        for (j = 0, ln = paramObjects.length; j < ln; j++) {
            paramObject = paramObjects[j];
            value = paramObject.value;

            if (Ext.isEmpty(value)) {
                value = '';
            }
            else if (Ext.isDate(value)) {
                value = Ext.Date.toString(value);
            }

            params.push(encodeURIComponent(paramObject.name) + '=' + encodeURIComponent(String(value)));
        }

        return params.join('&');
    },

    
    fromQueryString: function(queryString, recursive) {
        var parts = queryString.replace(/^\?/, '').split('&'),
            object = {},
            temp, components, name, value, i, ln,
            part, j, subLn, matchedKeys, matchedName,
            keys, key, nextKey;

        for (i = 0, ln = parts.length; i < ln; i++) {
            part = parts[i];

            if (part.length > 0) {
                components = part.split('=');
                name = decodeURIComponent(components[0]);
                value = (components[1] !== undefined) ? decodeURIComponent(components[1]) : '';

                if (!recursive) {
                    if (object.hasOwnProperty(name)) {
                        if (!Ext.isArray(object[name])) {
                            object[name] = [object[name]];
                        }

                        object[name].push(value);
                    }
                    else {
                        object[name] = value;
                    }
                }
                else {
                    matchedKeys = name.match(/(\[):?([^\]]*)\]/g);
                    matchedName = name.match(/^([^\[]+)/);

                    if (!matchedName) {
                        throw new Error('[Ext.Object.fromQueryString] Malformed query string given, failed parsing name from "' + part + '"');
                    }

                    name = matchedName[0];
                    keys = [];

                    if (matchedKeys === null) {
                        object[name] = value;
                        continue;
                    }

                    for (j = 0, subLn = matchedKeys.length; j < subLn; j++) {
                        key = matchedKeys[j];
                        key = (key.length === 2) ? '' : key.substring(1, key.length - 1);
                        keys.push(key);
                    }

                    keys.unshift(name);

                    temp = object;

                    for (j = 0, subLn = keys.length; j < subLn; j++) {
                        key = keys[j];

                        if (j === subLn - 1) {
                            if (Ext.isArray(temp) && key === '') {
                                temp.push(value);
                            }
                            else {
                                temp[key] = value;
                            }
                        }
                        else {
                            if (temp[key] === undefined || typeof temp[key] === 'string') {
                                nextKey = keys[j+1];

                                temp[key] = (Ext.isNumeric(nextKey) || nextKey === '') ? [] : {};
                            }

                            temp = temp[key];
                        }
                    }
                }
            }
        }

        return object;
    },

    
    each: function(object, fn, scope) {
        for (var property in object) {
            if (object.hasOwnProperty(property)) {
                if (fn.call(scope || object, property, object[property], object) === false) {
                    return;
                }
            }
        }
    },

    
    merge: function(source) {
        var i = 1,
            ln = arguments.length,
            mergeFn = ExtObject.merge,
            cloneFn = Ext.clone,
            object, key, value, sourceKey;

        for (; i < ln; i++) {
            object = arguments[i];

            for (key in object) {
                value = object[key];
                if (value && value.constructor === Object) {
                    sourceKey = source[key];
                    if (sourceKey && sourceKey.constructor === Object) {
                        mergeFn(sourceKey, value);
                    }
                    else {
                        source[key] = cloneFn(value);
                    }
                }
                else {
                    source[key] = value;
                }
            }
        }

        return source;
    },

    
    mergeIf: function(source) {
        var i = 1,
            ln = arguments.length,
            cloneFn = Ext.clone,
            object, key, value;

        for (; i < ln; i++) {
            object = arguments[i];

            for (key in object) {
                if (!(key in source)) {
                    value = object[key];

                    if (value && value.constructor === Object) {
                        source[key] = cloneFn(value);
                    }
                    else {
                        source[key] = value;
                    }
                }
            }
        }

        return source;
    },

    
    getKey: function(object, value) {
        for (var property in object) {
            if (object.hasOwnProperty(property) && object[property] === value) {
                return property;
            }
        }

        return null;
    },

    
    getValues: function(object) {
        var values = [],
            property;

        for (property in object) {
            if (object.hasOwnProperty(property)) {
                values.push(object[property]);
            }
        }

        return values;
    },

    
    getKeys: ('keys' in Object.prototype) ? Object.keys : function(object) {
        var keys = [],
            property;

        for (property in object) {
            if (object.hasOwnProperty(property)) {
                keys.push(property);
            }
        }

        return keys;
    },

    
    getSize: function(object) {
        var size = 0,
            property;

        for (property in object) {
            if (object.hasOwnProperty(property)) {
                size++;
            }
        }

        return size;
    },

    
    classify: function(object) {
        var prototype = object,
            objectProperties = [],
            propertyClassesMap = {},
            objectClass = function() {
                var i = 0,
                    ln = objectProperties.length,
                    property;

                for (; i < ln; i++) {
                    property = objectProperties[i];
                    this[property] = new propertyClassesMap[property];
                }
            },
            key, value;

        for (key in object) {
            if (object.hasOwnProperty(key)) {
                value = object[key];

                if (value && value.constructor === Object) {
                    objectProperties.push(key);
                    propertyClassesMap[key] = ExtObject.classify(value);
                }
            }
        }

        objectClass.prototype = prototype;

        return objectClass;
    }
};



Ext.merge = Ext.Object.merge;


Ext.mergeIf = Ext.Object.mergeIf;


Ext.urlEncode = function() {
    var args = Ext.Array.from(arguments),
        prefix = '';

    
    if ((typeof args[1] === 'string')) {
        prefix = args[1] + '&';
        args[1] = false;
    }

    return prefix + ExtObject.toQueryString.apply(ExtObject, args);
};


Ext.urlDecode = function() {
    return ExtObject.fromQueryString.apply(ExtObject, arguments);
};

})();


Ext.Function = {

    
    flexSetter: function(fn) {
        return function(a, b) {
            var k, i;

            if (a === null) {
                return this;
            }

            if (typeof a !== 'string') {
                for (k in a) {
                    if (a.hasOwnProperty(k)) {
                        fn.call(this, k, a[k]);
                    }
                }

                if (Ext.enumerables) {
                    for (i = Ext.enumerables.length; i--;) {
                        k = Ext.enumerables[i];
                        if (a.hasOwnProperty(k)) {
                            fn.call(this, k, a[k]);
                        }
                    }
                }
            } else {
                fn.call(this, a, b);
            }

            return this;
        };
    },

    
    bind: function(fn, scope, args, appendArgs) {
        if (arguments.length === 2) {
            return function() {
                return fn.apply(scope, arguments);
            }
        }

        var method = fn,
            slice = Array.prototype.slice;

        return function() {
            var callArgs = args || arguments;

            if (appendArgs === true) {
                callArgs = slice.call(arguments, 0);
                callArgs = callArgs.concat(args);
            }
            else if (typeof appendArgs == 'number') {
                callArgs = slice.call(arguments, 0); 
                Ext.Array.insert(callArgs, appendArgs, args);
            }

            return method.apply(scope || window, callArgs);
        };
    },

    
    pass: function(fn, args, scope) {
        if (!Ext.isArray(args)) {
            args = Ext.Array.clone(args);
        }

        return function() {
            args.push.apply(args, arguments);
            return fn.apply(scope || this, args);
        };
    },

    
    alias: function(object, methodName) {
        return function() {
            return object[methodName].apply(object, arguments);
        };
    },

    
    clone: function(method) {
        return function() {
            return method.apply(this, arguments);
        };
    },

    
    createInterceptor: function(origFn, newFn, scope, returnValue) {
        var method = origFn;
        if (!Ext.isFunction(newFn)) {
            return origFn;
        }
        else {
            return function() {
                var me = this,
                    args = arguments;
                newFn.target = me;
                newFn.method = origFn;
                return (newFn.apply(scope || me || window, args) !== false) ? origFn.apply(me || window, args) : returnValue || null;
            };
        }
    },

    
    createDelayed: function(fn, delay, scope, args, appendArgs) {
        if (scope || args) {
            fn = Ext.Function.bind(fn, scope, args, appendArgs);
        }

        return function() {
            var me = this,
                args = Array.prototype.slice.call(arguments);

            setTimeout(function() {
                fn.apply(me, args);
            }, delay);
        }
    },

    
    defer: function(fn, millis, scope, args, appendArgs) {
        fn = Ext.Function.bind(fn, scope, args, appendArgs);
        if (millis > 0) {
            return setTimeout(fn, millis);
        }
        fn();
        return 0;
    },

    
    createSequence: function(originalFn, newFn, scope) {
        if (!newFn) {
            return originalFn;
        }
        else {
            return function() {
                var result = originalFn.apply(this, arguments);
                newFn.apply(scope || this, arguments);
                return result;
            };
        }
    },

    
    createBuffered: function(fn, buffer, scope, args) {
        var timerId;

        return function() {
            if (!scope) {
                scope = this;
            }

            if (!args) {
                args = Array.prototype.slice.call(arguments);
            }

            if (timerId) {
                clearTimeout(timerId);
                timerId = null;
            }

            timerId = setTimeout(function(){
                fn.apply(scope, args);
            }, buffer);
        };
    },

    
    createThrottled: function(fn, interval, scope) {
        var lastCallTime, elapsed, lastArgs, timer, execute = function() {
            fn.apply(scope || this, lastArgs);
            lastCallTime = new Date().getTime();
        };

        return function() {
            elapsed = new Date().getTime() - lastCallTime;
            lastArgs = arguments;

            clearTimeout(timer);
            if (!lastCallTime || (elapsed >= interval)) {
                execute();
            } else {
                timer = setTimeout(execute, interval - elapsed);
            }
        };
    },

    interceptBefore: function(object, methodName, fn) {
        var method = object[methodName] || Ext.emptyFn;

        return object[methodName] = function() {
            var ret = fn.apply(this, arguments);
            method.apply(this, arguments);

            return ret;
        };
    },

    interceptAfter: function(object, methodName, fn) {
        var method = object[methodName] || Ext.emptyFn;

        return object[methodName] = function() {
            method.apply(this, arguments);
            return fn.apply(this, arguments);
        };
    }
};


Ext.defer = Ext.Function.alias(Ext.Function, 'defer');


Ext.pass = Ext.Function.alias(Ext.Function, 'pass');


Ext.bind = Ext.Function.alias(Ext.Function, 'bind');


Ext.JSON = new(function() {
    var useHasOwn = !! {}.hasOwnProperty,
    isNative = function() {
        var useNative = null;

        return function() {
            if (useNative === null) {
                useNative = Ext.USE_NATIVE_JSON && window.JSON && JSON.toString() == '[object JSON]';
            }

            return useNative;
        };
    }(),
    pad = function(n) {
        return n < 10 ? "0" + n : n;
    },
    doDecode = function(json) {
        return eval("(" + json + ')');
    },
    doEncode = function(o) {
        if (!Ext.isDefined(o) || o === null) {
            return "null";
        } else if (Ext.isArray(o)) {
            return encodeArray(o);
        } else if (Ext.isDate(o)) {
            return Ext.JSON.encodeDate(o);
        } else if (Ext.isString(o)) {
            return encodeString(o);
        } else if (typeof o == "number") {
            
            return isFinite(o) ? String(o) : "null";
        } else if (Ext.isBoolean(o)) {
            return String(o);
        } else if (Ext.isObject(o)) {
            return encodeObject(o);
        } else if (typeof o === "function") {
            return "null";
        }
        return 'undefined';
    },
    m = {
        "\b": '\\b',
        "\t": '\\t',
        "\n": '\\n',
        "\f": '\\f',
        "\r": '\\r',
        '"': '\\"',
        "\\": '\\\\',
        '\x0b': '\\u000b' 
    },
    charToReplace = /[\\\"\x00-\x1f\x7f-\uffff]/g,
    encodeString = function(s) {
        return '"' + s.replace(charToReplace, function(a) {
            var c = m[a];
            return typeof c === 'string' ? c : '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
        }) + '"';
    },
    encodeArray = function(o) {
        var a = ["[", ""],
        
        len = o.length,
        i;
        for (i = 0; i < len; i += 1) {
            a.push(doEncode(o[i]), ',');
        }
        
        a[a.length - 1] = ']';
        return a.join("");
    },
    encodeObject = function(o) {
        var a = ["{", ""],
        
        i;
        for (i in o) {
            if (!useHasOwn || o.hasOwnProperty(i)) {
                a.push(doEncode(i), ":", doEncode(o[i]), ',');
            }
        }
        
        a[a.length - 1] = '}';
        return a.join("");
    };

    
    this.encodeDate = function(o) {
        return '"' + o.getFullYear() + "-" 
        + pad(o.getMonth() + 1) + "-"
        + pad(o.getDate()) + "T"
        + pad(o.getHours()) + ":"
        + pad(o.getMinutes()) + ":"
        + pad(o.getSeconds()) + '"';
    };

    
    this.encode = function() {
        var ec;
        return function(o) {
            if (!ec) {
                
                ec = isNative() ? JSON.stringify : doEncode;
            }
            return ec(o);
        };
    }();


    
    this.decode = function() {
        var dc;
        return function(json, safe) {
            if (!dc) {
                
                dc = isNative() ? JSON.parse : doDecode;
            }
            try {
                return dc(json);
            } catch (e) {
                if (safe === true) {
                    return null;
                }
                Ext.Error.raise({
                    sourceClass: "Ext.JSON",
                    sourceMethod: "decode",
                    msg: "You're trying to decode an invalid JSON String: " + json
                });
            }
        };
    }();

})();

Ext.encode = Ext.JSON.encode;

Ext.decode = Ext.JSON.decode;



Ext.Date = {
    now: Date.now,

    
    toString: function(date) {
        if (!date) {
            date = new Date();
        }

        var pad = Ext.String.leftPad;

        return date.getFullYear() + "-"
            + pad(date.getMonth() + 1, 2, '0') + "-"
            + pad(date.getDate(), 2, '0') + "T"
            + pad(date.getHours(), 2, '0') + ":"
            + pad(date.getMinutes(), 2, '0') + ":"
            + pad(date.getSeconds(), 2, '0');
    }
};

Ext.merge(Ext, {
	util: {
		Date: Ext.Date
	}
});

Ext.Error = {
    raise: function(error) {
        throw new Error(error.msg);
    }
};


(function(flexSetter) {

var noArgs = [],
    Base = function(){};

    
    Ext.apply(Base, {
        $className: 'Ext.Base',

        $isClass: true,

        
        create: function() {
            return Ext.create.apply(Ext, [this].concat(Array.prototype.slice.call(arguments, 0)));
        },

        
        extend: function(parent) {
            var parentPrototype = parent.prototype,
                basePrototype, prototype, i, ln, name, statics;

            prototype = this.prototype = Ext.Object.chain(parentPrototype);
            prototype.self = this;

            this.superclass = prototype.superclass = parentPrototype;

            if (!parent.$isClass) {
                basePrototype = Ext.Base.prototype;

                for (i in basePrototype) {
                    if (i in prototype) {
                        prototype[i] = basePrototype[i];
                    }
                }
            }

            
            statics = parentPrototype.$inheritableStatics;

            if (statics) {
                for (i = 0,ln = statics.length; i < ln; i++) {
                    name = statics[i];

                    if (!this.hasOwnProperty(name)) {
                        this[name] = parent[name];
                    }
                }
            }

            if (parent.$onExtended) {
                this.$onExtended = parent.$onExtended.slice();
            }

            prototype.config = new prototype.configClass;
            prototype.initConfigList = prototype.initConfigList.slice();
            prototype.hasInitConfigMap = Ext.clone(prototype.hasInitConfigMap);
            prototype.hasConfigMap = Ext.Object.chain(prototype.hasConfigMap);
        },

        
        '$onExtended': [],

        
        triggerExtended: function() {
            var callbacks = this.$onExtended,
                ln = callbacks.length,
                i, callback;

            if (ln > 0) {
                for (i = 0; i < ln; i++) {
                    callback = callbacks[i];
                    callback.fn.apply(callback.scope || this, arguments);
                }
            }
        },

        
        onExtended: function(fn, scope) {
            this.$onExtended.push({
                fn: fn,
                scope: scope
            });

            return this;
        },

        
        addConfig: function(config, fullMerge) {
            var prototype = this.prototype,
                configNameCache = Ext.Class.configNameCache,
                hasConfig = prototype.hasConfigMap,
                configList = prototype.initConfigList,
                configMap = prototype.hasInitConfigMap,
                defaultConfig = prototype.config,
                initializedName, name, value;

            for (name in config) {
                if (config.hasOwnProperty(name)) {
                    if (!hasConfig[name]) {
                        hasConfig[name] = true;
                    }

                    value = config[name];

                    initializedName = configNameCache[name].initialized;

                    if (!configMap[name] && value !== null && !prototype[initializedName]) {
                        configMap[name] = true;
                        configList.push(name);
                    }
                }
            }

            if (fullMerge) {
                Ext.merge(defaultConfig, config);
            }
            else {
                Ext.mergeIf(defaultConfig, config);
            }

            prototype.configClass = Ext.Object.classify(defaultConfig);
        },

        
        addStatics: function(members) {
            var member, name;
            var className = Ext.getClassName(this);

            for (name in members) {
                if (members.hasOwnProperty(name)) {
                    member = members[name];
                    if (typeof member == 'function') {
                        member.displayName = className + '.' + name;
                    }
                    this[name] = member;
                }
            }

            return this;
        },

        
        addInheritableStatics: function(members) {
            var inheritableStatics,
                hasInheritableStatics,
                prototype = this.prototype,
                name, member;

            inheritableStatics = prototype.$inheritableStatics;
            hasInheritableStatics = prototype.$hasInheritableStatics;

            if (!inheritableStatics) {
                inheritableStatics = prototype.$inheritableStatics = [];
                hasInheritableStatics = prototype.$hasInheritableStatics = {};
            }

            var className = Ext.getClassName(this);

            for (name in members) {
                if (members.hasOwnProperty(name)) {
                    member = members[name];
                    if (typeof member == 'function') {
                        member.displayName = className + '.' + name;
                    }
                    this[name] = member;

                    if (!hasInheritableStatics[name]) {
                        hasInheritableStatics[name] = true;
                        inheritableStatics.push(name);
                    }
                }
            }

            return this;
        },

        
        addMembers: function(members) {
            var prototype = this.prototype,
                enumerables = Ext.enumerables,
                names = [],
                i, ln, name, member;

            var className = this.$className || '';

            for (name in members) {
                names.push(name);
            }

            if (enumerables) {
                names.push.apply(names, enumerables);
            }

            for (i = 0,ln = names.length; i < ln; i++) {
                name = names[i];

                if (members.hasOwnProperty(name)) {
                    member = members[name];

                    if (typeof member == 'function' && !member.$isClass && member !== Ext.emptyFn) {
                        member.$owner = this;
                        member.$name = name;
                        member.displayName = className + '#' + name;
                    }

                    prototype[name] = member;
                }
            }

            return this;
        },

        
        addMember: function(name, member) {
            if (typeof member == 'function' && !member.$isClass && member !== Ext.emptyFn) {
                member.$owner = this;
                member.$name = name;
                member.displayName = (this.$className || '') + '#' + name;
            }

            this.prototype[name] = member;

            return this;
        },

        
        implement: function() {
            this.addMembers.apply(this, arguments);
        },

        
        borrow: function(fromClass, members) {
            var prototype = this.prototype,
                fromPrototype = fromClass.prototype,
                className = Ext.getClassName(this),
                i, ln, name, fn, toBorrow;

            members = Ext.Array.from(members);

            for (i = 0,ln = members.length; i < ln; i++) {
                name = members[i];

                toBorrow = fromPrototype[name];

                if (typeof toBorrow == 'function') {
                    fn = function() {
                        return toBorrow.apply(this, arguments);
                    };

                    if (className) {
                        fn.displayName = className + '#' + name;
                    }

                    fn.$owner = this;
                    fn.$name = name;

                    prototype[name] = fn;
                }
                else {
                    prototype[name] = toBorrow;
                }
            }

            return this;
        },

        
        override: function(members) {
            var me = this,
                enumerables = Ext.enumerables,
                target = me.prototype,
                cloneFunction = Ext.Function.clone,
                name, index, member, statics, names, previous;

            if (arguments.length === 2) {
                name = members;
                members = {};
                members[name] = arguments[1];
                enumerables = null;
            }

            do {
                names = []; 
                statics = null; 

                for (name in members) { 
                    if (name == 'statics') {
                        statics = members[name];
                    } else {
                        names.push(name);
                    }
                }

                if (enumerables) {
                    names.push.apply(names, enumerables);
                }

                for (index = names.length; index--; ) {
                    name = names[index];

                    if (members.hasOwnProperty(name)) {
                        member = members[name];

                        if (typeof member == 'function' && !member.$className && member !== Ext.emptyFn) {
                            if (typeof member.$owner != 'undefined') {
                                member = cloneFunction(member);
                            }

                            var className = me.$className;
                            if (className) {
                                member.displayName = className + '#' + name;
                            }

                            member.$owner = me;
                            member.$name = name;

                            previous = target[name];
                            if (previous) {
                                member.$previous = previous;
                            }
                        }

                        target[name] = member;
                    }
                }

                target = me; 
                members = statics; 
            } while (members);

            return this;
        },

        
        callParent: function(args) {
            var method;

            
            return (method = this.callParent.caller) && (method.$previous ||
                  ((method = method.$owner ? method : method.caller) &&
                        method.$owner.superclass.$class[method.$name])).apply(this, args || noArgs);
        },

        
        mixin: function(name, mixinClass) {
            var mixin = mixinClass.prototype,
                prototype = this.prototype,
                key;

            if (typeof mixin.onClassMixedIn != 'undefined') {
                mixin.onClassMixedIn.call(mixinClass, this);
            }

            if (!prototype.hasOwnProperty('mixins')) {
                if ('mixins' in prototype) {
                    prototype.mixins = Ext.Object.chain(prototype.mixins);
                }
                else {
                    prototype.mixins = {};
                }
            }

            for (key in mixin) {
                if (key === 'mixins') {
                    Ext.merge(prototype.mixins, mixin[key]);
                }
                else if (typeof prototype[key] == 'undefined' && key != 'mixinId' && key != 'config') {
                    prototype[key] = mixin[key];
                }
            }

            if ('config' in mixin) {
                this.addConfig(mixin.config, false);
            }

            prototype.mixins[name] = mixin;
        },

        
        getName: function() {
            return Ext.getClassName(this);
        },

        
        createAlias: flexSetter(function(alias, origin) {
            this.override(alias, function() {
                return this[origin].apply(this, arguments);
            });
        }),

        
        addXtype: function(xtype) {
            var prototype = this.prototype,
                xtypesMap = prototype.xtypesMap,
                xtypes = prototype.xtypes,
                xtypesChain = prototype.xtypesChain;

            if (!prototype.hasOwnProperty('xtypesMap')) {
                xtypesMap = prototype.xtypesMap = Ext.merge({}, prototype.xtypesMap || {});
                xtypes = prototype.xtypes = prototype.xtypes ? [].concat(prototype.xtypes) : [];
                xtypesChain = prototype.xtypesChain = prototype.xtypesChain ? [].concat(prototype.xtypesChain) : [];
                prototype.xtype = xtype;
            }

            if (!xtypesMap[xtype]) {
                xtypesMap[xtype] = true;
                xtypes.push(xtype);
                xtypesChain.push(xtype);
                Ext.ClassManager.setAlias(this, 'widget.' + xtype);
            }

            return this;
        }
    });

    Base.implement({
        isInstance: true,

        $className: 'Ext.Base',

        configClass: Ext.emptyFn,

        initConfigList: [],

        hasConfigMap: {},

        hasInitConfigMap: {},

        
        statics: function() {
            var method = this.statics.caller,
                self = this.self;

            if (!method) {
                return self;
            }

            return method.$owner;
        },

        
        callParent: function(args) {
            
            
            
            
            var method,
                superMethod = (method = this.callParent.caller) && (method.$previous ||
                        ((method = method.$owner ? method : method.caller) &&
                                method.$owner.superclass[method.$name]));

            if (!superMethod) {
                method = this.callParent.caller;
                var parentClass, methodName;

                if (!method.$owner) {
                    if (!method.caller) {
                        throw new Error("Attempting to call a protected method from the public scope, which is not allowed");
                    }

                    method = method.caller;
                }

                parentClass = method.$owner.superclass;
                methodName = method.$name;

                if (!(methodName in parentClass)) {
                    throw new Error("this.callParent() was called but there's no such method (" + methodName +
                                ") found in the parent class (" + (Ext.getClassName(parentClass) || 'Object') + ")");
                }
            }

            return superMethod.apply(this, args || noArgs);
        },

        
        self: Base,

        
        constructor: function() {
            return this;
        },

        
        initConfig: function(config) {
            var instanceConfig = config,
                configNameCache = Ext.Class.configNameCache,
                defaultConfig = new this.configClass,
                defaultConfigList = this.initConfigList,
                hasConfig = this.hasConfigMap,
                nameMap, i, ln, name, initializedName, value;

            this.initConfig = Ext.emptyFn;

            this.initialConfig = instanceConfig || {};

            this.config = config = (instanceConfig) ? Ext.merge(defaultConfig, config) : defaultConfig;

            if (instanceConfig) {
                defaultConfigList = defaultConfigList.slice();

                for (name in instanceConfig) {
                    if (hasConfig[name]) {
                        if (instanceConfig[name] !== null) {
                            defaultConfigList.push(name);
                            this[configNameCache[name].initialized] = false;
                        }
                    }
                }
            }

            for (i = 0,ln = defaultConfigList.length; i < ln; i++) {
                name = defaultConfigList[i];
                nameMap = configNameCache[name];
                initializedName = nameMap.initialized;

                if (!this[initializedName]) {
                    this[initializedName] = true;
                    this[nameMap.set](config[name]);
                }
            }

            return this;
        },

        
        hasConfig: function(name) {
            return Boolean(this.hasConfigMap[name]);
        },

        
        setConfig: function(config, applyIfNotSet) {
            if (!config) {
                return this;
            }

            var configNameCache = Ext.Class.configNameCache,
                currentConfig = this.config,
                hasConfig = this.hasConfigMap,
                initialConfig = this.initialConfig,
                name, value;

            applyIfNotSet = Boolean(applyIfNotSet);

            for (name in config) {
                if (applyIfNotSet && initialConfig.hasOwnProperty(name)) {
                    continue;
                }

                value = config[name];
                currentConfig[name] = value;

                if (hasConfig[name]) {
                    this[configNameCache[name].set](value);
                }
            }

            return this;
        },

        
        getConfig: function(name) {
            var configNameCache = Ext.Class.configNameCache;

            return this[configNameCache[name].get]();
        },

        
        getInitialConfig: function(name) {
            var config = this.config;

            if (!name) {
                return config;
            }
            else {
                return config[name];
            }
        },

        
        onConfigUpdate: function(names, callback, scope) {
            var self = this.self,
                className = self.$className,
                i, ln, name,
                updaterName, updater, newUpdater;

            names = Ext.Array.from(names);

            scope = scope || this;

            for (i = 0,ln = names.length; i < ln; i++) {
                name = names[i];
                updaterName = 'update' + Ext.String.capitalize(name);
                updater = this[updaterName] || Ext.emptyFn;
                newUpdater = function() {
                    updater.apply(this, arguments);
                    scope[callback].apply(scope, arguments);
                };
                newUpdater.$name = updaterName;
                newUpdater.$owner = self;
                newUpdater.displayName = className + '#' + updaterName;

                this[updaterName] = newUpdater;
            }
        },

        destroy: function() {
            this.destroy = Ext.emptyFn;
        }
    });

    
    Base.prototype.callOverridden = Base.prototype.callParent;

    Ext.Base = Base;

})(Ext.Function.flexSetter);


(function() {
    var ExtClass,
        Base = Ext.Base,
        baseStaticMembers = [],
        baseStaticMember, baseStaticMemberLength;

    for (baseStaticMember in Base) {
        if (Base.hasOwnProperty(baseStaticMember)) {
            baseStaticMembers.push(baseStaticMember);
        }
    }

    baseStaticMemberLength = baseStaticMembers.length;

    
    Ext.Class = ExtClass = function(Class, data, onCreated) {
        if (typeof Class != 'function') {
            onCreated = data;
            data = Class;
            Class = null;
        }

        if (!data) {
            data = {};
        }

        Class = ExtClass.create(Class);

        ExtClass.process(Class, data, onCreated);

        return Class;
    };

    Ext.apply(ExtClass, {
        
        onBeforeCreated: function(Class, data, hooks) {
            Class.addMembers(data);

            hooks.onCreated.call(Class, Class);
        },

        
        create: function(Class) {
            var name, i;

            if (!Class) {
                Class = function() {
                    return this.constructor.apply(this, arguments);
                };
            }

            for (i = 0; i < baseStaticMemberLength; i++) {
                name = baseStaticMembers[i];
                Class[name] = Base[name];
            }

            return Class;
        },

        
        process: function(Class, data, onCreated) {
            var preprocessorStack = data.preprocessors || ExtClass.defaultPreprocessors,
                registeredPreprocessors = this.preprocessors,
                hooks = {
                    onBeforeCreated: this.onBeforeCreated
                },
                index = 0,
                preprocessors = [],
                preprocessor, preprocessorsProperties,
                i, ln, j, subLn, preprocessorProperty, process;

            delete data.preprocessors;

            for (i = 0,ln = preprocessorStack.length; i < ln; i++) {
                preprocessor = preprocessorStack[i];

                if (typeof preprocessor == 'string') {
                    preprocessor = registeredPreprocessors[preprocessor];
                    preprocessorsProperties = preprocessor.properties;

                    if (preprocessorsProperties === true) {
                        preprocessors.push(preprocessor.fn);
                    }
                    else if (preprocessorsProperties) {
                        for (j = 0,subLn = preprocessorsProperties.length; j < subLn; j++) {
                            preprocessorProperty = preprocessorsProperties[j];

                            if (data.hasOwnProperty(preprocessorProperty)) {
                                preprocessors.push(preprocessor.fn);
                                break;
                            }
                        }
                    }
                }
                else {
                    preprocessors.push(preprocessor);
                }
            }

            hooks.onCreated = onCreated ? onCreated : Ext.emptyFn;

            process = function(Class, data, hooks) {
                preprocessor = preprocessors[index++];

                if (!preprocessor) {
                    hooks.onBeforeCreated.apply(this, arguments);
                    return;
                }

                if (preprocessor.call(this, Class, data, hooks, process) !== false) {
                    process.apply(this, arguments);
                }
            };

            process.call(this, Class, data, hooks);
        },

        
        preprocessors: {},

        
        registerPreprocessor: function(name, fn, properties, position, relativeTo) {
            if (!position) {
                position = 'last';
            }

            if (!properties) {
                properties = [name];
            }

            this.preprocessors[name] = {
                name: name,
                properties: properties || false,
                fn: fn
            };

            this.setDefaultPreprocessorPosition(name, position, relativeTo);

            return this;
        },

        
        getPreprocessor: function(name) {
            return this.preprocessors[name];
        },

        
        getPreprocessors: function() {
            return this.preprocessors;
        },

        
        defaultPreprocessors: [],

        
        getDefaultPreprocessors: function() {
            return this.defaultPreprocessors;
        },

        
        setDefaultPreprocessors: function(preprocessors) {
            this.defaultPreprocessors = Ext.Array.from(preprocessors);

            return this;
        },

        
        setDefaultPreprocessorPosition: function(name, offset, relativeName) {
            var defaultPreprocessors = this.defaultPreprocessors,
                index;

            if (typeof offset == 'string') {
                if (offset === 'first') {
                    defaultPreprocessors.unshift(name);

                    return this;
                }
                else if (offset === 'last') {
                    defaultPreprocessors.push(name);

                    return this;
                }

                offset = (offset === 'after') ? 1 : -1;
            }

            index = Ext.Array.indexOf(defaultPreprocessors, relativeName);

            if (index !== -1) {
                Ext.Array.splice(defaultPreprocessors, Math.max(0, index + offset), 0, name);
            }

            return this;
        },

        configNameCache: {},

        getConfigNameMap: function(name) {
            var cache = this.configNameCache,
                map = cache[name],
                capitalizedName;

            if (!map) {
                capitalizedName = name.charAt(0).toUpperCase() + name.substr(1);

                map = cache[name] = {
                    internal: '_' + name,
                    initialized: '_is' + capitalizedName + 'Initialized',
                    apply: 'apply' + capitalizedName,
                    update: 'update' + capitalizedName,
                    'set': 'set' + capitalizedName,
                    'get': 'get' + capitalizedName,
                    doSet : 'doSet' + capitalizedName,
                    changeEvent: name.toLowerCase() + 'change'
                }
            }

            return map;
        }
    });

    
    ExtClass.registerPreprocessor('extend', function(Class, data) {
        var Base = Ext.Base,
            basePrototype = Base.prototype,
            extend = data.extend,
            Parent, parentPrototype, i;

        delete data.extend;

        if (extend && extend !== Object) {
            Parent = extend;
        }
        else {
            Parent = Base;
        }

        parentPrototype = Parent.prototype;

        if (!Parent.$isClass) {
            for (i in basePrototype) {
                if (!parentPrototype[i]) {
                    parentPrototype[i] = basePrototype[i];
                }
            }
        }

        Class.extend(Parent);

        Class.triggerExtended.apply(Class, arguments);

        if (data.onClassExtended) {
            Class.onExtended(data.onClassExtended);
            delete data.onClassExtended;
        }

    }, true);

    
    ExtClass.registerPreprocessor('statics', function(Class, data) {
        Class.addStatics(data.statics);

        delete data.statics;
    });

    
    ExtClass.registerPreprocessor('inheritableStatics', function(Class, data) {
        Class.addInheritableStatics(data.inheritableStatics);

        delete data.inheritableStatics;
    });

    
    ExtClass.registerPreprocessor('config', function(Class, data) {
        var config = data.config,
            prototype = Class.prototype;

        delete data.config;

        Ext.Object.each(config, function(name, value) {
            var nameMap = ExtClass.getConfigNameMap(name),
                internalName = nameMap.internal,
                initializedName = nameMap.initialized,
                applyName = nameMap.apply,
                updateName = nameMap.update,
                setName = nameMap.set,
                getName = nameMap.get,
                hasOwnSetter = (setName in prototype) || data.hasOwnProperty(setName),
                hasOwnApplier = (applyName in prototype) || data.hasOwnProperty(applyName),
                hasOwnUpdater = (updateName in prototype) || data.hasOwnProperty(updateName),
                optimizedGetter, customGetter;

            if (value === null || (!hasOwnSetter && !hasOwnApplier && !hasOwnUpdater)) {
                prototype[internalName] = value;
                prototype[initializedName] = true;
            }
            else {
                prototype[initializedName] = false;
            }

            if (!hasOwnSetter) {
                data[setName] = function(value) {
                    var oldValue = this[internalName],
                        applier = this[applyName],
                        updater = this[updateName];

                    if (!this[initializedName]) {
                        this[initializedName] = true;
                    }

                    if (applier) {
                        value = applier.call(this, value, oldValue);
                    }

                    if (typeof value != 'undefined') {
                        this[internalName] = value;

                        if (updater && value !== oldValue) {
                            updater.call(this, value, oldValue);
                        }
                    }

                    return this;
                }
            }

            if (!(getName in prototype) || data.hasOwnProperty(getName)) {
                customGetter = data[getName] || false;

                if (customGetter) {
                    optimizedGetter = function() {
                        return customGetter.apply(this, arguments);
                    };
                }
                else {
                    optimizedGetter = function() {
                        return this[internalName];
                    };
                }

                data[getName] = function() {
                    var currentGetter;

                    if (!this[initializedName]) {
                        this[initializedName] = true;
                        this[setName](this.config[name]);
                    }

                    currentGetter = this[getName];

                    if ('$previous' in currentGetter) {
                        currentGetter.$previous = optimizedGetter;
                    }
                    else {
                        this[getName] = optimizedGetter;
                    }

                    return optimizedGetter.apply(this, arguments);
                };
            }
        });

        Class.addConfig(config, true);
    });

    
    ExtClass.registerPreprocessor('mixins', function(Class, data, hooks) {
        var mixins = data.mixins,
            name, mixin, i, ln;

        delete data.mixins;

        Ext.Function.interceptBefore(hooks, 'onCreated', function() {
            if (mixins instanceof Array) {
                for (i = 0,ln = mixins.length; i < ln; i++) {
                    mixin = mixins[i];
                    name = mixin.prototype.mixinId || mixin.$className;

                    Class.mixin(name, mixin);
                }
            }
            else {
                for (name in mixins) {
                    if (mixins.hasOwnProperty(name)) {
                        Class.mixin(name, mixins[name]);
                    }
                }
            }
        });
    });

    
    Ext.extend = function(Class, Parent, members) {
        if (arguments.length === 2 && Ext.isObject(Parent)) {
            members = Parent;
            Parent = Class;
            Class = null;
        }

        var cls;

        if (!Parent) {
            throw new Error("[Ext.extend] Attempting to extend from a class which has not been loaded on the page.");
        }

        members.extend = Parent;
        members.preprocessors = [
            'extend'
            ,'statics'
            ,'inheritableStatics'
            ,'mixins'
            ,'config'
        ];

        if (Class) {
            cls = new ExtClass(Class, members);
        }
        else {
            cls = new ExtClass(members);
        }

        cls.prototype.override = function(o) {
            for (var m in o) {
                if (o.hasOwnProperty(m)) {
                    this[m] = o[m];
                }
            }
        };

        return cls;
    };

})();


(function(Class, alias, arraySlice, arrayFrom, global) {

    var Manager = Ext.ClassManager = {

        
        classes: {},

        
        existCache: {},

        
        namespaceRewrites: [{
            from: 'Ext.',
            to: Ext
        }],

        
        maps: {
            alternateToName: {},
            aliasToName: {},
            nameToAliases: {},
            nameToAlternates: {},
            overridesByName: {}
        },

        
        enableNamespaceParseCache: true,

        
        namespaceParseCache: {},

        
        instantiators: [],

        
        isCreated: function(className) {
            var existCache = this.existCache,
                i, ln, part, root, parts;

            if (typeof className != 'string' || className.length < 1) {
                throw new Error("[Ext.ClassManager] Invalid classname, must be a string and must not be empty");
            }

            if (this.classes[className] || existCache[className]) {
                return true;
            }

            root = global;
            parts = this.parseNamespace(className);

            for (i = 0, ln = parts.length; i < ln; i++) {
                part = parts[i];

                if (typeof part != 'string') {
                    root = part;
                } else {
                    if (!root || !root[part]) {
                        return false;
                    }

                    root = root[part];
                }
            }

            existCache[className] = true;

            this.triggerCreated(className);

            return true;
        },

        
        createdListeners: [],

        
        nameCreatedListeners: {},

        
        triggerCreated: function(className) {
            var listeners = this.createdListeners,
                nameListeners = this.nameCreatedListeners,
                i, ln, listener;

            for (i = 0,ln = listeners.length; i < ln; i++) {
                listener = listeners[i];
                listener.fn.call(listener.scope, className);
            }

            listeners = nameListeners[className];

            if (listeners) {
                for (i = 0,ln = listeners.length; i < ln; i++) {
                    listener = listeners[i];
                    listener.fn.call(listener.scope, className);
                }

                delete nameListeners[className];
            }
        },

        
        onCreated: function(fn, scope, className) {
            var listeners = this.createdListeners,
                nameListeners = this.nameCreatedListeners,
                listener = {
                    fn: fn,
                    scope: scope
                };

            if (className) {
                if (this.isCreated(className)) {
                    fn.call(scope, className);
                    return;
                }

                if (!nameListeners[className]) {
                    nameListeners[className] = [];
                }

                nameListeners[className].push(listener);
            }
            else {
                listeners.push(listener);
            }
        },

        
        parseNamespace: function(namespace) {
            if (typeof namespace != 'string') {
                throw new Error("[Ext.ClassManager] Invalid namespace, must be a string");
            }

            var cache = this.namespaceParseCache;

            if (this.enableNamespaceParseCache) {
                if (cache.hasOwnProperty(namespace)) {
                    return cache[namespace];
                }
            }

            var parts = [],
                rewrites = this.namespaceRewrites,
                root = global,
                name = namespace,
                rewrite, from, to, i, ln;

            for (i = 0, ln = rewrites.length; i < ln; i++) {
                rewrite = rewrites[i];
                from = rewrite.from;
                to = rewrite.to;

                if (name === from || name.substring(0, from.length) === from) {
                    name = name.substring(from.length);

                    if (typeof to != 'string') {
                        root = to;
                    } else {
                        parts = parts.concat(to.split('.'));
                    }

                    break;
                }
            }

            parts.push(root);

            parts = parts.concat(name.split('.'));

            if (this.enableNamespaceParseCache) {
                cache[namespace] = parts;
            }

            return parts;
        },

        
        setNamespace: function(name, value) {
            var root = global,
                parts = this.parseNamespace(name),
                ln = parts.length - 1,
                leaf = parts[ln],
                i, part;

            for (i = 0; i < ln; i++) {
                part = parts[i];

                if (typeof part != 'string') {
                    root = part;
                } else {
                    if (!root[part]) {
                        root[part] = {};
                    }

                    root = root[part];
                }
            }

            root[leaf] = value;

            return root[leaf];
        },

        
        createNamespaces: function() {
            var root = global,
                parts, part, i, j, ln, subLn;

            for (i = 0, ln = arguments.length; i < ln; i++) {
                parts = this.parseNamespace(arguments[i]);

                for (j = 0, subLn = parts.length; j < subLn; j++) {
                    part = parts[j];

                    if (typeof part != 'string') {
                        root = part;
                    } else {
                        if (!root[part]) {
                            root[part] = {};
                        }

                        root = root[part];
                    }
                }
            }

            return root;
        },

        
        set: function(name, value) {
            var me = this,
                maps = me.maps,
                nameToAlternates = maps.nameToAlternates,
                targetName = me.getName(value),
                alternates;

            me.classes[name] = me.setNamespace(name, value);

            if (targetName && targetName !== name) {
                maps.alternateToName[name] = targetName;
                alternates = nameToAlternates[targetName] || (nameToAlternates[targetName] = []);
                alternates.push(name);
            }

            return this;
        },

        
        get: function(name) {
            var classes = this.classes;

            if (classes[name]) {
                return classes[name];
            }

            var root = global,
                parts = this.parseNamespace(name),
                part, i, ln;

            for (i = 0, ln = parts.length; i < ln; i++) {
                part = parts[i];

                if (typeof part != 'string') {
                    root = part;
                } else {
                    if (!root || !root[part]) {
                        return null;
                    }

                    root = root[part];
                }
            }

            return root;
        },

        
        setAlias: function(cls, alias) {
            var aliasToNameMap = this.maps.aliasToName,
                nameToAliasesMap = this.maps.nameToAliases,
                className;

            if (typeof cls == 'string') {
                className = cls;
            } else {
                className = this.getName(cls);
            }

            if (alias && aliasToNameMap[alias] !== className) {
                if (aliasToNameMap[alias] && Ext.isDefined(global.console)) {
                    global.console.log("[Ext.ClassManager] Overriding existing alias: '" + alias + "' " +
                        "of: '" + aliasToNameMap[alias] + "' with: '" + className + "'. Be sure it's intentional.");
                }

                aliasToNameMap[alias] = className;
            }

            if (!nameToAliasesMap[className]) {
                nameToAliasesMap[className] = [];
            }

            if (alias) {
                Ext.Array.include(nameToAliasesMap[className], alias);
            }

            return this;
        },

        
        getByAlias: function(alias) {
            return this.get(this.getNameByAlias(alias));
        },

        
        getNameByAlias: function(alias) {
            return this.maps.aliasToName[alias] || '';
        },

        
        getNameByAlternate: function(alternate) {
            return this.maps.alternateToName[alternate] || '';
        },

        
        getAliasesByName: function(name) {
            return this.maps.nameToAliases[name] || [];
        },

        
        getName: function(object) {
            return object && object.$className || '';
        },

        
        getClass: function(object) {
            return object && object.self || null;
        },

        
        applyOverrides: function (name) {
            var me = this,
                overridesByName = me.maps.overridesByName,
                overrides = overridesByName[name],
                length = overrides && overrides.length || 0,
                createOverride = me.createOverride,
                i;

            delete overridesByName[name];

            for (i = 0; i < length; ++i) {
                createOverride.apply(me, overrides[i]);
            }
        },

        
        create: function(className, data, createdFn) {
            if (typeof className != 'string') {
                throw new Error("[Ext.define] Invalid class name '" + className + "' specified, must be a non-empty string");
            }

            data.$className = className;

            return new Class(data, function() {
                var postprocessorStack = data.postprocessors || Manager.defaultPostprocessors,
                    registeredPostprocessors = Manager.postprocessors,
                    index = 0,
                    postprocessors = [],
                    postprocessor, process, i, ln, j, subLn, postprocessorProperties, postprocessorProperty,
                    alternateNames;

                delete data.postprocessors;

                for (i = 0,ln = postprocessorStack.length; i < ln; i++) {
                    postprocessor = postprocessorStack[i];

                    if (typeof postprocessor == 'string') {
                        postprocessor = registeredPostprocessors[postprocessor];
                        postprocessorProperties = postprocessor.properties;

                        if (postprocessorProperties === true) {
                            postprocessors.push(postprocessor.fn);
                        }
                        else if (postprocessorProperties) {
                            for (j = 0,subLn = postprocessorProperties.length; j < subLn; j++) {
                                postprocessorProperty = postprocessorProperties[j];

                                if (data.hasOwnProperty(postprocessorProperty)) {
                                    postprocessors.push(postprocessor.fn);
                                    break;
                                }
                            }
                        }
                    }
                    else {
                        postprocessors.push(postprocessor);
                    }
                }

                process = function(clsName, cls, clsData) {
                    postprocessor = postprocessors[index++];

                    if (!postprocessor) {
                        Manager.set(className, cls);

                        if (createdFn) {
                            createdFn.call(cls, cls);
                        }

                        Manager.triggerCreated(className);
                        return;
                    }

                    if (postprocessor.call(this, clsName, cls, clsData, process) !== false) {
                        process.apply(this, arguments);
                    }
                };

                process.call(Manager, className, this, data);

                
                Manager.applyOverrides(className);
                alternateNames = Manager.maps.nameToAlternates[className];

                for (i = 0, ln = alternateNames && alternateNames.length || 0; i < ln; ++i) {
                    Manager.applyOverrides(alternateNames[i]);
                }
            });
        },

        createOverride: function (overrideName, data, createdFn) {
            var me = this,
                className = data.override,
                cls = me.get(className),
                overrideBody, overridesByName, overrides;

            if (cls) {
                
                
                

                
                overrideBody = Ext.apply({}, data);
                delete overrideBody.requires;
                delete overrideBody.uses;
                delete overrideBody.override;

                me.create(overrideName, {
                        constructor: function () {
                            throw new Error("Cannot create instance of override '" + overrideName + "'");
                        },
                        requires: data.requires,
                        uses: data.uses,
                        override: className
                    }, function () {
                        this.active = true;
                        if (cls.override) { 
                            cls.override(overrideBody);
                        } else { 
                            cls.self.override(overrideBody);
                        }

                        if (createdFn) {
                            
                            
                            
                            createdFn.call(cls);
                        }
                    });
            } else {
                
                
                
                
                overridesByName = me.maps.overridesByName;
                overrides = overridesByName[className] || (overridesByName[className] = []);
                overrides.push(Array.prototype.slice.call(arguments, 0));

                
                
                me.setNamespace(overrideName, {
                    override: className
                });
            }
        },

        
        instantiateByAlias: function() {
            var alias = arguments[0],
                args = arraySlice.call(arguments),
                className = this.getNameByAlias(alias);

            if (!className) {
                className = this.maps.aliasToName[alias];

                if (!className) {
                    throw new Error("[Ext.createByAlias] Cannot create an instance of unrecognized alias: " + alias);
                }

                if (global.console) {
                    global.console.warn("[Ext.Loader] Synchronously loading '" + className + "'; consider adding " +
                         "Ext.require('" + alias + "') above Ext.onReady");
                }

                Ext.syncRequire(className);
            }

            args[0] = className;

            return this.instantiate.apply(this, args);
        },

        
        instantiate: function() {
            var name = arguments[0],
                args = arraySlice.call(arguments, 1),
                alias = name,
                possibleName, cls;

            if (typeof name != 'function') {
                if ((typeof name != 'string' || name.length < 1)) {
                    throw new Error("[Ext.create] Invalid class name or alias '" + name + "' specified, must be a non-empty string");
                }

                cls = this.get(name);
            }
            else {
                cls = name;
            }

            
            if (!cls) {
                possibleName = this.getNameByAlias(name);

                if (possibleName) {
                    name = possibleName;

                    cls = this.get(name);
                }
            }

            
            if (!cls) {
                possibleName = this.getNameByAlternate(name);

                if (possibleName) {
                    name = possibleName;

                    cls = this.get(name);
                }
            }

            
            if (!cls) {
                if (global.console) {
                    global.console.warn("[Ext.Loader] Synchronously loading '" + name + "'; consider adding " +
                         "Ext.require('" + ((possibleName) ? alias : name) + "') above Ext.onReady");
                }

                Ext.syncRequire(name);

                cls = this.get(name);
            }

            if (!cls) {
                throw new Error("[Ext.create] Cannot create an instance of unrecognized class name / alias: " + alias);
            }

            if (typeof cls != 'function') {
                throw new Error("[Ext.create] '" + name + "' is a singleton and cannot be instantiated");
            }

            return this.getInstantiator(args.length)(cls, args);
        },

        
        dynInstantiate: function(name, args) {
            args = arrayFrom(args, true);
            args.unshift(name);

            return this.instantiate.apply(this, args);
        },

        
        getInstantiator: function(length) {
            var instantiators = this.instantiators,
                instantiator;

            instantiator = instantiators[length];

            if (!instantiator) {
                var i = length,
                    args = [];

                for (i = 0; i < length; i++) {
                    args.push('a[' + i + ']');
                }

                instantiator = instantiators[length] = new Function('c', 'a', 'return new c(' + args.join(',') + ')');
                instantiator.displayName = "Ext.ClassManager.instantiate" + length;
            }

            return instantiator;
        },

        
        postprocessors: {},

        
        defaultPostprocessors: [],

        
        registerPostprocessor: function(name, fn, properties, position, relativeTo) {
            if (!position) {
                position = 'last';
            }

            if (!properties) {
                properties = [name];
            }

            this.postprocessors[name] = {
                name: name,
                properties: properties || false,
                fn: fn
            };

            this.setDefaultPostprocessorPosition(name, position, relativeTo);

            return this;
        },

        
        setDefaultPostprocessors: function(postprocessors) {
            this.defaultPostprocessors = arrayFrom(postprocessors);

            return this;
        },

        
        setDefaultPostprocessorPosition: function(name, offset, relativeName) {
            var defaultPostprocessors = this.defaultPostprocessors,
                index;

            if (typeof offset == 'string') {
                if (offset === 'first') {
                    defaultPostprocessors.unshift(name);

                    return this;
                }
                else if (offset === 'last') {
                    defaultPostprocessors.push(name);

                    return this;
                }

                offset = (offset === 'after') ? 1 : -1;
            }

            index = Ext.Array.indexOf(defaultPostprocessors, relativeName);

            if (index !== -1) {
                Ext.Array.splice(defaultPostprocessors, Math.max(0, index + offset), 0, name);
            }

            return this;
        },

        
        getNamesByExpression: function(expression) {
            var nameToAliasesMap = this.maps.nameToAliases,
                names = [],
                name, alias, aliases, possibleName, regex, i, ln;

            if (typeof expression != 'string' || expression.length < 1) {
                throw new Error("[Ext.ClassManager.getNamesByExpression] Expression " + expression + " is invalid, must be a non-empty string");
            }

            if (expression.indexOf('*') !== -1) {
                expression = expression.replace(/\*/g, '(.*?)');
                regex = new RegExp('^' + expression + '$');

                for (name in nameToAliasesMap) {
                    if (nameToAliasesMap.hasOwnProperty(name)) {
                        aliases = nameToAliasesMap[name];

                        if (name.search(regex) !== -1) {
                            names.push(name);
                        }
                        else {
                            for (i = 0, ln = aliases.length; i < ln; i++) {
                                alias = aliases[i];

                                if (alias.search(regex) !== -1) {
                                    names.push(name);
                                    break;
                                }
                            }
                        }
                    }
                }

            } else {
                possibleName = this.getNameByAlias(expression);

                if (possibleName) {
                    names.push(possibleName);
                } else {
                    possibleName = this.getNameByAlternate(expression);

                    if (possibleName) {
                        names.push(possibleName);
                    } else {
                        names.push(expression);
                    }
                }
            }

            return names;
        }
    };

    
    Manager.registerPostprocessor('alias', function(name, cls, data) {
        var aliases = data.alias,
            i, ln;

        for (i = 0,ln = aliases.length; i < ln; i++) {
            alias = aliases[i];

            this.setAlias(cls, alias);
        }

    }, ['xtype', 'alias']);

    
    Manager.registerPostprocessor('singleton', function(name, cls, data, fn) {
        fn.call(this, name, new cls(), data);
        return false;
    });

    
    Manager.registerPostprocessor('alternateClassName', function(name, cls, data) {
        var alternates = data.alternateClassName,
            i, ln, alternate;

        if (!(alternates instanceof Array)) {
            alternates = [alternates];
        }

        for (i = 0, ln = alternates.length; i < ln; i++) {
            alternate = alternates[i];

            if (typeof alternate != 'string') {
                throw new Error("[Ext.define] Invalid alternate of: '" + alternate + "' for class: '" + name + "'; must be a valid string");
            }

            this.set(alternate, cls);
        }
    });

    Ext.apply(Ext, {
        
        create: alias(Manager, 'instantiate'),

        
        widget: function(name) {
            var args = arraySlice.call(arguments);
            args[0] = 'widget.' + name;

            return Manager.instantiateByAlias.apply(Manager, args);
        },

        
        createByAlias: alias(Manager, 'instantiateByAlias'),

        
        define: function (className, data, createdFn) {
            if (data.override) {
                return Manager.createOverride.apply(Manager, arguments);
            }

            return Manager.create.apply(Manager, arguments);
        },

        
        getClassName: alias(Manager, 'getName'),

        
        getDisplayName: function(object) {
            if (object) {
                if (object.displayName) {
                    return object.displayName;
                }

                if (object.$name && object.$class) {
                    return Ext.getClassName(object.$class) + '#' + object.$name;
                }

                if (object.$className) {
                    return object.$className;
                }
            }

            return 'Anonymous';
        },

        
        getClass: alias(Manager, 'getClass'),

        
        namespace: alias(Manager, 'createNamespaces')
    });

    
    Ext.createWidget = Ext.widget;

    
    Ext.ns = Ext.namespace;

    Class.registerPreprocessor('className', function(cls, data) {
        if (data.$className) {
            cls.$className = data.$className;
            cls.displayName = cls.$className;
        }
    }, true, 'first');

    Class.registerPreprocessor('alias', function(cls, data) {
        var prototype = cls.prototype,
            xtypes = arrayFrom(data.xtype),
            aliases = arrayFrom(data.alias),
            widgetPrefix = 'widget.',
            widgetPrefixLength = widgetPrefix.length,
            xtypesChain = Array.prototype.slice.call(prototype.xtypesChain || []),
            xtypesMap = Ext.merge({}, prototype.xtypesMap || {}),
            i, ln, alias, xtype;

        for (i = 0,ln = aliases.length; i < ln; i++) {
            alias = aliases[i];

            if (typeof alias != 'string' || alias.length < 1) {
                throw new Error("[Ext.define] Invalid alias of: '" + alias + "' for class: '" + name + "'; must be a valid string");
            }

            if (alias.substring(0, widgetPrefixLength) === widgetPrefix) {
                xtype = alias.substring(widgetPrefixLength);
                Ext.Array.include(xtypes, xtype);
            }
        }

        cls.xtype = data.xtype = xtypes[0];
        data.xtypes = xtypes;

        for (i = 0,ln = xtypes.length; i < ln; i++) {
            xtype = xtypes[i];

            if (!xtypesMap[xtype]) {
                xtypesMap[xtype] = true;
                xtypesChain.push(xtype);
            }
        }

        data.xtypesChain = xtypesChain;
        data.xtypesMap = xtypesMap;

        Ext.Function.interceptAfter(data, 'onClassCreated', function() {
            var mixins = prototype.mixins,
                key, mixin;

            for (key in mixins) {
                if (mixins.hasOwnProperty(key)) {
                    mixin = mixins[key];

                    xtypes = mixin.xtypes;

                    if (xtypes) {
                        for (i = 0,ln = xtypes.length; i < ln; i++) {
                            xtype = xtypes[i];

                            if (!xtypesMap[xtype]) {
                                xtypesMap[xtype] = true;
                                xtypesChain.push(xtype);
                            }
                        }
                    }
                }
            }
        });

        for (i = 0,ln = xtypes.length; i < ln; i++) {
            xtype = xtypes[i];

            if (typeof xtype != 'string' || xtype.length < 1) {
                throw new Error("[Ext.define] Invalid xtype of: '" + xtype + "' for class: '" + name + "'; must be a valid non-empty string");
            }

            Ext.Array.include(aliases, widgetPrefix + xtype);
        }

        data.alias = aliases;

    }, ['xtype', 'alias']);

})(Ext.Class, Ext.Function.alias, Array.prototype.slice, Ext.Array.from, Ext.global);



(function(Manager, Class, flexSetter, alias, pass, arrayFrom, arrayErase, arrayInclude) {

    var
        dependencyProperties = ['extend', 'mixins', 'requires'],
        Loader;

    Loader = Ext.Loader = {

        
        isInHistory: {},

        
        history: [],

        
        config: {
            
            enabled: false,

            
            disableCaching: true,

            
            disableCachingParam: '_dc',

            
            paths: {
                'Ext': '.'
            }
        },

        
        setConfig: function(name, value) {
            if (Ext.isObject(name) && arguments.length === 1) {
                Ext.merge(this.config, name);
            }
            else {
                this.config[name] = (Ext.isObject(value)) ? Ext.merge(this.config[name], value) : value;
            }

            return this;
        },

        
        getConfig: function(name) {
            if (name) {
                return this.config[name];
            }

            return this.config;
        },

        
        setPath: flexSetter(function(name, path) {
            this.config.paths[name] = path;

            return this;
        }),

        
        getPath: function(className) {
            var path = '',
                paths = this.config.paths,
                prefix = this.getPrefix(className);

            if (prefix.length > 0) {
                if (prefix === className) {
                    return paths[prefix];
                }

                path = paths[prefix];
                className = className.substring(prefix.length + 1);
            }

            if (path.length > 0) {
                path += '/';
            }

            return path.replace(/\/\.\//g, '/') + className.replace(/\./g, "/") + '.js';
        },

        
        getPrefix: function(className) {
            var paths = this.config.paths,
                prefix, deepestPrefix = '';

            if (paths.hasOwnProperty(className)) {
                return className;
            }

            for (prefix in paths) {
                if (paths.hasOwnProperty(prefix) && prefix + '.' === className.substring(0, prefix.length + 1)) {
                    if (prefix.length > deepestPrefix.length) {
                        deepestPrefix = prefix;
                    }
                }
            }

            return deepestPrefix;
        },

        
        require: function(expressions, fn, scope, excludes) {
            if (fn) {
                fn.call(scope);
            }
        },

        
        syncRequire: function() {},

        
        exclude: function(excludes) {
            var me = this;

            return {
                require: function(expressions, fn, scope) {
                    return me.require(expressions, fn, scope, excludes);
                },

                syncRequire: function(expressions, fn, scope) {
                    return me.syncRequire(expressions, fn, scope, excludes);
                }
            };
        },

        
        onReady: function(fn, scope, withDomReady, options) {
            var oldFn;

            if (withDomReady !== false && Ext.onDocumentReady) {
                oldFn = fn;

                fn = function() {
                    Ext.onDocumentReady(oldFn, scope, options);
                };
            }

            fn.call(scope);
        }
    };

    Ext.apply(Loader, {
        
        documentHead: typeof document != 'undefined' && (document.head || document.getElementsByTagName('head')[0]),

        
        isLoading: false,

        
        queue: [],

        
        isClassFileLoaded: {},

        
        isFileLoaded: {},

        
        readyListeners: [],

        
        optionalRequires: [],

        
        requiresMap: {},

        
        numPendingFiles: 0,

        
        numLoadedFiles: 0,

        
        hasFileLoadError: false,

        
        classNameToFilePathMap: {},

        
        syncModeEnabled: false,

        scriptElements: {},

        
        refreshQueue: function() {
            var queue = this.queue,
                ln = queue.length,
                i, item, j, requires, references;

            if (ln === 0) {
                this.triggerReady();
                return;
            }

            for (i = 0; i < ln; i++) {
                item = queue[i];

                if (item) {
                    requires = item.requires;
                    references = item.references;

                    
                    
                    if (requires.length > this.numLoadedFiles) {
                        continue;
                    }

                    j = 0;

                    do {
                        if (Manager.isCreated(requires[j])) {
                            
                            arrayErase(requires, j, 1);
                        }
                        else {
                            j++;
                        }
                    } while (j < requires.length);

                    if (item.requires.length === 0) {
                        arrayErase(queue, i, 1);
                        item.callback.call(item.scope);
                        this.refreshQueue();
                        break;
                    }
                }
            }

            return this;
        },

        
        injectScriptElement: function(url, onLoad, onError, scope) {
            var script = document.createElement('script'),
                me = this,
                onLoadFn = function() {
                    me.cleanupScriptElement(script);
                    onLoad.call(scope);
                },
                onErrorFn = function() {
                    me.cleanupScriptElement(script);
                    onError.call(scope);
                };

            script.type = 'text/javascript';
            script.src = url;
            script.onload = onLoadFn;
            script.onerror = onErrorFn;
            script.onreadystatechange = function() {
                if (this.readyState === 'loaded' || this.readyState === 'complete') {
                    onLoadFn();
                }
            };

            this.documentHead.appendChild(script);

            return script;
        },

        removeScriptElement: function(url) {
            var scriptElements = this.scriptElements;

            if (scriptElements[url]) {
                this.cleanupScriptElement(scriptElements[url], true);
                delete scriptElements[url];
            }

            return this;
        },

        
        cleanupScriptElement: function(script, remove) {
            script.onload = null;
            script.onreadystatechange = null;
            script.onerror = null;

            if (remove) {
                this.documentHead.removeChild(script);
            }

            return this;
        },

        
        loadScriptFile: function(url, onLoad, onError, scope, synchronous) {
            var me = this,
                isFileLoaded = this.isFileLoaded,
                scriptElements = this.scriptElements,
                noCacheUrl = url + (this.getConfig('disableCaching') ? ('?' + this.getConfig('disableCachingParam') + '=' + Ext.Date.now()) : ''),
                isCrossOriginRestricted = false,
                xhr, status, onScriptError;

            if (isFileLoaded[url]) {
                return this;
            }

            scope = scope || this;

            this.isLoading = true;

            if (!synchronous) {
                onScriptError = function() {
                    onError.call(scope, "Failed loading '" + url + "', please verify that the file exists", synchronous);
                };

                if (!Ext.isReady && Ext.onDocumentReady) {
                    Ext.onDocumentReady(function() {
                        if (!isFileLoaded[url]) {
                            scriptElements[url] = me.injectScriptElement(noCacheUrl, onLoad, onScriptError, scope);
                        }
                    });
                }
                else {
                    scriptElements[url] = this.injectScriptElement(noCacheUrl, onLoad, onScriptError, scope);
                }
            }
            else {
                if (typeof XMLHttpRequest != 'undefined') {
                    xhr = new XMLHttpRequest();
                } else {
                    xhr = new ActiveXObject('Microsoft.XMLHTTP');
                }

                try {
                    xhr.open('GET', noCacheUrl, false);
                    xhr.send(null);
                } catch (e) {
                    isCrossOriginRestricted = true;
                }

                status = (xhr.status === 1223) ? 204 : xhr.status;

                if (!isCrossOriginRestricted) {
                    isCrossOriginRestricted = (status === 0);
                }

                if (isCrossOriginRestricted
                ) {
                    onError.call(this, "Failed loading synchronously via XHR: '" + url + "'; It's likely that the file is either " +
                                       "being loaded from a different domain or from the local file system whereby cross origin " +
                                       "requests are not allowed due to security reasons. Use asynchronous loading with " +
                                       "Ext.require instead.", synchronous);
                }
                else if (status >= 200 && status < 300
                ) {
                    
                    
                    Ext.globalEval(xhr.responseText + "\n//@ sourceURL=" + url);

                    onLoad.call(scope);
                }
                else {
                    onError.call(this, "Failed loading synchronously via XHR: '" + url + "'; please " +
                                       "verify that the file exists. " +
                                       "XHR status code: " + status, synchronous);
                }

                
                xhr = null;
            }
        },

        
        syncRequire: function() {
            var syncModeEnabled = this.syncModeEnabled;

            if (!syncModeEnabled) {
                this.syncModeEnabled = true;
            }

            this.require.apply(this, arguments);

            if (!syncModeEnabled) {
                this.syncModeEnabled = false;
            }

            this.refreshQueue();
        },

        
        require: function(expressions, fn, scope, excludes) {
            var excluded = {},
                included = {},
                queue = this.queue,
                classNameToFilePathMap = this.classNameToFilePathMap,
                isClassFileLoaded = this.isClassFileLoaded,
                excludedClassNames = [],
                possibleClassNames = [],
                classNames = [],
                references = [],
                callback,
                syncModeEnabled,
                filePath, expression, exclude, className,
                possibleClassName, i, j, ln, subLn;

            if (excludes) {
                excludes = arrayFrom(excludes);

                for (i = 0,ln = excludes.length; i < ln; i++) {
                    exclude = excludes[i];

                    if (typeof exclude == 'string' && exclude.length > 0) {
                        excludedClassNames = Manager.getNamesByExpression(exclude);

                        for (j = 0,subLn = excludedClassNames.length; j < subLn; j++) {
                            excluded[excludedClassNames[j]] = true;
                        }
                    }
                }
            }

            expressions = arrayFrom(expressions);

            if (fn) {
                if (fn.length > 0) {
                    callback = function() {
                        var classes = [],
                            i, ln, name;

                        for (i = 0,ln = references.length; i < ln; i++) {
                            name = references[i];
                            classes.push(Manager.get(name));
                        }

                        return fn.apply(this, classes);
                    };
                }
                else {
                    callback = fn;
                }
            }
            else {
                callback = Ext.emptyFn;
            }

            scope = scope || Ext.global;

            for (i = 0,ln = expressions.length; i < ln; i++) {
                expression = expressions[i];

                if (typeof expression == 'string' && expression.length > 0) {
                    possibleClassNames = Manager.getNamesByExpression(expression);
                    subLn = possibleClassNames.length;

                    for (j = 0; j < subLn; j++) {
                        possibleClassName = possibleClassNames[j];

                        if (excluded[possibleClassName] !== true) {
                            references.push(possibleClassName);

                            if (!Manager.isCreated(possibleClassName) && !included[possibleClassName]) {
                                included[possibleClassName] = true;
                                classNames.push(possibleClassName);
                            }
                        }
                    }
                }
            }

            
            
            if (classNames.length > 0) {
                if (!this.config.enabled) {
                    throw new Error("Ext.Loader is not enabled, so dependencies cannot be resolved dynamically. " +
                             "Missing required class" + ((classNames.length > 1) ? "es" : "") + ": " + classNames.join(', '));
                }
            }
            else {
                callback.call(scope);
                return this;
            }

            syncModeEnabled = this.syncModeEnabled;

            if (!syncModeEnabled) {
                queue.push({
                    requires: classNames.slice(), 
                                                  
                    callback: callback,
                    scope: scope
                });
            }

            ln = classNames.length;

            for (i = 0; i < ln; i++) {
                className = classNames[i];

                filePath = this.getPath(className);

                
                
                
                if (syncModeEnabled && isClassFileLoaded.hasOwnProperty(className)) {
                    this.numPendingFiles--;
                    this.removeScriptElement(filePath);
                    delete isClassFileLoaded[className];
                }

                if (!isClassFileLoaded.hasOwnProperty(className)) {
                    isClassFileLoaded[className] = false;

                    classNameToFilePathMap[className] = filePath;

                    this.numPendingFiles++;

                    this.loadScriptFile(
                        filePath,
                        pass(this.onFileLoaded, [className, filePath], this),
                        pass(this.onFileLoadError, [className, filePath]),
                        this,
                        syncModeEnabled
                    );
                }
            }

            if (syncModeEnabled) {
                callback.call(scope);

                if (ln === 1) {
                    return Manager.get(className);
                }
            }

            return this;
        },

        
        onFileLoaded: function(className, filePath) {
            this.numLoadedFiles++;

            this.isClassFileLoaded[className] = true;
            this.isFileLoaded[filePath] = true;

            this.numPendingFiles--;

            if (this.numPendingFiles === 0) {
                this.refreshQueue();
            }

            if (!this.syncModeEnabled && this.numPendingFiles === 0 && this.isLoading && !this.hasFileLoadError) {
                var queue = this.queue,
                    missingClasses = [],
                    missingPaths = [],
                    requires,
                    i, ln, j, subLn;

                for (i = 0,ln = queue.length; i < ln; i++) {
                    requires = queue[i].requires;

                    for (j = 0,subLn = requires.length; j < subLn; j++) {
                        if (this.isClassFileLoaded[requires[j]]) {
                            missingClasses.push(requires[j]);
                        }
                    }
                }

                if (missingClasses.length < 1) {
                    return;
                }

                missingClasses = Ext.Array.filter(Ext.Array.unique(missingClasses), function(item) {
                    return !this.requiresMap.hasOwnProperty(item);
                }, this);

                for (i = 0,ln = missingClasses.length; i < ln; i++) {
                    missingPaths.push(this.classNameToFilePathMap[missingClasses[i]]);
                }

                throw new Error("The following classes are not declared even if their files have been " +
                            "loaded: '" + missingClasses.join("', '") + "'. Please check the source code of their " +
                            "corresponding files for possible typos: '" + missingPaths.join("', '"));
            }
        },

        
        onFileLoadError: function(className, filePath, errorMessage, isSynchronous) {
            this.numPendingFiles--;
            this.hasFileLoadError = true;

            throw new Error("[Ext.Loader] " + errorMessage);
        },

        
        addOptionalRequires: function(requires) {
            var optionalRequires = this.optionalRequires,
                i, ln, require;

            requires = arrayFrom(requires);

            for (i = 0, ln = requires.length; i < ln; i++) {
                require = requires[i];

                arrayInclude(optionalRequires, require);
            }

            return this;
        },

        
        triggerReady: function(force) {
            var readyListeners = this.readyListeners,
                optionalRequires = this.optionalRequires,
                listener;

            if (this.isLoading || force) {
                this.isLoading = false;

                if (optionalRequires.length !== 0) {
                    
                    optionalRequires = optionalRequires.slice();

                    
                    this.optionalRequires.length = 0;

                    this.require(optionalRequires, pass(this.triggerReady, [true], this), this);
                    return this;
                }

                while (readyListeners.length) {
                    listener = readyListeners.shift();
                    listener.fn.call(listener.scope);

                    if (this.isLoading) {
                        return this;
                    }
                }
            }

            return this;
        },

        
        onReady: function(fn, scope, withDomReady, options) {
            var oldFn;

            if (withDomReady !== false && Ext.onDocumentReady) {
                oldFn = fn;

                fn = function() {
                    Ext.onDocumentReady(oldFn, scope, options);
                };
            }

            if (!this.isLoading) {
                fn.call(scope);
            }
            else {
                this.readyListeners.push({
                    fn: fn,
                    scope: scope
                });
            }
        },

        
        historyPush: function(className) {
            var isInHistory = this.isInHistory;

            if (className && this.isClassFileLoaded.hasOwnProperty(className) && !isInHistory[className]) {
                isInHistory[className] = true;
                this.history.push(className);
            }

            return this;
        }
    });


    
    Ext.require = alias(Loader, 'require');

    
    Ext.syncRequire = alias(Loader, 'syncRequire');

    
    Ext.exclude = alias(Loader, 'exclude');

    
    Ext.onReady = function(fn, scope, options) {
        Loader.onReady(fn, scope, true, options);
    };

    Class.registerPreprocessor('loader', function(cls, data, hooks, continueFn) {
        var me = this,
            dependencies = [],
            className = Manager.getName(cls),
            i, j, ln, subLn, value, propertyName, propertyValue;

        

        for (i = 0,ln = dependencyProperties.length; i < ln; i++) {
            propertyName = dependencyProperties[i];

            if (data.hasOwnProperty(propertyName)) {
                propertyValue = data[propertyName];

                if (typeof propertyValue == 'string') {
                    dependencies.push(propertyValue);
                }
                else if (propertyValue instanceof Array) {
                    for (j = 0, subLn = propertyValue.length; j < subLn; j++) {
                        value = propertyValue[j];

                        if (typeof value == 'string') {
                            dependencies.push(value);
                        }
                    }
                }
                else if (typeof propertyValue != 'function') {
                    for (j in propertyValue) {
                        if (propertyValue.hasOwnProperty(j)) {
                            value = propertyValue[j];

                            if (typeof value == 'string') {
                                dependencies.push(value);
                            }
                        }
                    }
                }
            }
        }

        if (dependencies.length === 0) {
            return;
        }

        var deadlockPath = [],
            requiresMap = Loader.requiresMap,
            detectDeadlock;

        

        if (className) {
            requiresMap[className] = dependencies;
            if (!Loader.requiredByMap) Loader.requiredByMap = {};
            Ext.Array.each(dependencies, function(dependency){
                if (!Loader.requiredByMap[dependency]) Loader.requiredByMap[dependency] = [];
                Loader.requiredByMap[dependency].push(className);
            });
            detectDeadlock = function(cls) {
                deadlockPath.push(cls);

                if (requiresMap[cls]) {
                    if (Ext.Array.contains(requiresMap[cls], className)) {
                        throw new Error("Deadlock detected while loading dependencies! '" + className + "' and '" +
                                deadlockPath[1] + "' " + "mutually require each other. Path: " +
                                deadlockPath.join(' -> ') + " -> " + deadlockPath[0]);
                    }

                    for (i = 0,ln = requiresMap[cls].length; i < ln; i++) {
                        detectDeadlock(requiresMap[cls][i]);
                    }
                }
            };

            detectDeadlock(className);
        }


        Loader.require(dependencies, function() {
            for (i = 0,ln = dependencyProperties.length; i < ln; i++) {
                propertyName = dependencyProperties[i];

                if (data.hasOwnProperty(propertyName)) {
                    propertyValue = data[propertyName];

                    if (typeof propertyValue == 'string') {
                        data[propertyName] = Manager.get(propertyValue);
                    }
                    else if (propertyValue instanceof Array) {
                        for (j = 0, subLn = propertyValue.length; j < subLn; j++) {
                            value = propertyValue[j];

                            if (typeof value == 'string') {
                                data[propertyName][j] = Manager.get(value);
                            }
                        }
                    }
                    else if (typeof propertyValue != 'function') {
                        for (var k in propertyValue) {
                            if (propertyValue.hasOwnProperty(k)) {
                                value = propertyValue[k];

                                if (typeof value == 'string') {
                                    data[propertyName][k] = Manager.get(value);
                                }
                            }
                        }
                    }
                }
            }

            continueFn.call(me, cls, data, hooks);
        });

        return false;
    }, true, 'after', 'className');

    
    Manager.registerPostprocessor('uses', function(name, cls, data) {
        var uses = arrayFrom(data.uses),
            items = [],
            i, ln, item;

        for (i = 0,ln = uses.length; i < ln; i++) {
            item = uses[i];

            if (typeof item == 'string') {
                items.push(item);
            }
        }

        Loader.addOptionalRequires(items);
    });

    Manager.onCreated(function(className) {
        this.historyPush(className);
    }, Loader);

})(Ext.ClassManager, Ext.Class, Ext.Function.flexSetter, Ext.Function.alias,
   Ext.Function.pass, Ext.Array.from, Ext.Array.erase, Ext.Array.include);






Ext.ns('Ext.core');
Ext.core.EventManager =
Ext.EventManager = {
    addListener: function(element, eventName, fn, scope, options) {
        Ext.Logger.deprecate("Ext.EventManager.addListener is deprecated, use addListener() directly from an instance of Ext.Element instead", 2);
        element.on(eventName, fn, scope, options);
    },

    removeListener: function(element, eventName, fn, scope) {
        Ext.Logger.deprecate("Ext.EventManager.removeListener is deprecated, use removeListener() directly from an instance of Ext.Element instead", 2);
        element.un(eventName, fn, scope);
    },

    removeAll: function(element){
        Ext.Logger.deprecate("Ext.EventManager.removeAll is deprecated, use clearListeners() directly from an instance of Ext.Element instead", 3);
        Ext.get(element).clearListeners();
    },

    onWindowResize: function(fn, scope, options) {
        Ext.Logger.deprecate("Ext.EventManager.onWindowResize is deprecated, attach listener to Ext.Viewport instead, i.e: Ext.Viewport.on('resize', ...)", 2);
        Ext.Viewport.on('resize', fn, scope, options);
    },

    onOrientationChange: function(fn, scope, options) {
        Ext.Logger.deprecate("Ext.EventManager.onOrientationChange is deprecated, attach listener to Ext.Viewport instead, i.e: Ext.Viewport.on('orientationchange', ...)", 2);
        Ext.Viewport.on('orientationchange', fn, scope, options);
    },

    unOrientationChange: function(fn, scope, options) {
        Ext.Logger.deprecate("Ext.EventManager.unOrientationChange is deprecated, remove listener from Ext.Viewport instead, i.e: Ext.Viewport.un('orientationchange', ...)", 2);
        Ext.Viewport.un('orientationchange', fn, scope, options);
    }
};

Ext.EventManager.on = Ext.EventManager.addListener;
Ext.EventManager.un = Ext.EventManager.removeListener;


Ext.setVersion('touch', '2.0.0.pr2');

Ext.apply(Ext, {
    
    version: Ext.getVersion('touch'),

    
    idSeed: 0,

    
    repaint: function() {
        var mask = Ext.getBody().createChild({
            cls: Ext.baseCSSPrefix + 'mask ' + Ext.baseCSSPrefix + 'mask-transparent'
        });
        setTimeout(function() {
            mask.remove();
        }, 0);
    },

    
    id: function(el, prefix) {
        if (el && el.id) {
            return el.id;
        }

        el = Ext.getDom(el) || {};

        if (el === document || el === document.documentElement) {
            el.id = 'ext-application';
        }
        else if (el === document.body) {
            el.id = 'ext-viewport';
        }
        else if (el === window) {
            el.id = 'ext-window';
        }

        el.id = el.id || ((prefix || 'ext-element-') + (++Ext.idSeed));

        return el.id;
    },

    
    getBody: function() {
        if (!Ext.documentBodyElement) {
            if (!document.body) {
                throw new Error("[Ext.getBody] document.body does not exist at this point");
            }

            Ext.documentBodyElement = Ext.get(document.body);
        }

        return Ext.documentBodyElement;
    },

    
    getHead: function() {
        if (!Ext.documentHeadElement) {
            Ext.documentHeadElement = Ext.get(document.head || document.getElementsByTagName('head')[0]);
        }

        return Ext.documentHeadElement;
    },

    
    getDoc: function() {
        if (!Ext.documentElement) {
            Ext.documentElement = Ext.get(document);
        }

        return Ext.documentElement;
    },

    
    getCmp: function(id) {
        return Ext.ComponentMgr.get(id);
    },

    
    destroy: function() {
        var ln = arguments.length,
            i, arg;

        for (i = 0; i < ln; i++) {
            arg = arguments[i];
            if (arg) {
                if (Ext.isArray(arg)) {
                    this.destroy.apply(this, arg);
                }
                else if (Ext.isFunction(arg.destroy)) {
                    arg.destroy();
                }
                else if (arg.dom) {
                    arg.remove();
                }
            }
        }
    },

     
    getDom: function(el) {
        if (!el || !document) {
            return null;
        }

        return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
    },

    
    removeNode: function(node) {
        if (node && node.parentNode && node.tagName != 'BODY') {
            Ext.get(node).clearListeners();
            node.parentNode.removeChild(node);
            delete Ext.cache[node.id];
        }
    },

    defaultSetupConfig: {
        eventPublishers: {
            dom: {
                xclass: 'Ext.event.publisher.Dom'
            },
            touchGesture: {
                xclass: 'Ext.event.publisher.TouchGesture',
                moveThrottle: 3,
                recognizers: {
                    drag: {
                        xclass: 'Ext.event.recognizer.Drag'
                    },
                    tap: {
                        xclass: 'Ext.event.recognizer.Tap'
                    },
                    doubleTap: {
                        xclass: 'Ext.event.recognizer.DoubleTap'
                    },
                    longPress: {
                        xclass: 'Ext.event.recognizer.LongPress'
                    },
                    swipe: {
                        xclass: 'Ext.event.recognizer.HorizontalSwipe'
                    },
                    pinch: {
                        xclass: 'Ext.event.recognizer.Pinch'
                    },
                    rotate: {
                        xclass: 'Ext.event.recognizer.Rotate'
                    }
                }
            },
            componentDelegation: {
                xclass: 'Ext.event.publisher.ComponentDelegation'
            },
            componentPaint: {
                xclass: 'Ext.event.publisher.ComponentPaint'
            }
        },

        logger: {
            enabled: true,
            xclass: 'Ext.log.Logger',
            minPriority: 'deprecate',
            writers: {
                console: {
                    xclass: 'Ext.log.writer.Console',
                    throwOnErrors: true,
                    formatter: {
                        xclass: 'Ext.log.formatter.Default'
                    }
                }
            }
        },

        animator: {
            xclass: 'Ext.fx.Runner'
        },

        viewport: {
            xclass: 'Ext.viewport.Viewport'
        }
    },


    log: function(msg) {
        return Ext.Logger.log(msg);
    },

    setup: function(config) {
        var defaultSetupConfig = Ext.defaultSetupConfig,
            onReady = config.onReady || Ext.emptyFn,
            scope = config.scope,
            requires = Ext.Array.from(config.requires),
            extOnReady = Ext.onReady,
            callback, viewport;

        Ext.setup = function() {
            throw new Error("Ext.setup has already been called before");
        };

        delete config.requires;
        delete config.onReady;
        delete config.scope;

        requires.push('Ext.event.Dispatcher');
        requires.push('Ext.dom.CompositeElementLite'); 

        Ext.require(requires);

        callback = function() {
            Ext.onReady = extOnReady;
            Ext.onReady(onReady, scope);
        };

        Ext.onReady = function(fn, scope) {
            var origin = onReady;

            onReady = function() {
                origin();
                Ext.onReady(fn, scope);
            }
        };

        config = Ext.merge({}, defaultSetupConfig, config);

        Ext.onDocumentReady(function(){
            Ext.factoryConfig(config, function(data) {
                Ext.event.Dispatcher.getInstance().setPublishers(data.eventPublishers);

                if (data.logger) {
                    Ext.Logger = data.logger;
                }

                if (data.animator) {
                    Ext.Animator = data.animator;
                }

                if (data.viewport) {
                    Ext.Viewport = viewport = data.viewport;

                    Ext.getOrientation = function() {
                        Ext.Logger.deprecate("Ext.getOrientation() is deprecated, use Ext.Viewport.getOrientation() instead", 2);
                        return viewport.getOrientation();
                    };

                    Ext.Viewport.on('ready', callback, null, {single: true});
                }
                else {
                    callback();
                }
            });
        });

        if (!document.body) {
            
            document.write(
                '<meta id="extViewportMeta" ' +
                       'name="viewport" ' +
                       'content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no" />');
            document.write(
                '<meta name="apple-mobile-web-app-capable" content="yes">');
            document.write(
                '<meta name="apple-touch-fullscreen" content="yes">');
        }
    },

    
    application: function(config) {
        var onReady,
            scope;

        if (!config) {
            config = {};
        }

        config.requires = Ext.Array.from(config.requires);
        config.requires.push('Ext.app.Application');

        onReady = config.onReady;
        scope = config.scope;

        config.onReady = function() {
            new Ext.app.Application(config);

            if (onReady) {
                onReady.call(scope);
            }
        };

        Ext.setup(config);
    },

    
    factoryConfig: function(config, callback) {
        var isSimpleObject = Ext.isSimpleObject(config);

        if (isSimpleObject && config.xclass) {
            var className = config.xclass;

            delete config.xclass;

            Ext.require(className, function() {
                Ext.factoryConfig(config, function(cfg) {
                    callback(Ext.create(className, cfg));
                });
            });

            return;
        }

        var isArray = Ext.isArray(config),
            keys = [],
            key, value, i, ln;

        if (isSimpleObject || isArray) {
            if (isSimpleObject) {
                for (key in config) {
                    if (config.hasOwnProperty(key)) {
                        value = config[key];
                        if (Ext.isSimpleObject(value) || Ext.isArray(value)) {
                            keys.push(key);
                        }
                    }
                }
            }
            else {
                for (i = 0,ln = config.length; i < ln; i++) {
                    value = config[i];

                    if (Ext.isSimpleObject(value) || Ext.isArray(value)) {
                        keys.push(i);
                    }
                }
            }

            i = 0,
            ln = keys.length;

            if (ln === 0) {
                callback(config);
                return;
            }

            function fn(value) {
                config[key] = value;
                i++;
                factory();
            }

            function factory() {
                if (i >= ln) {
                    callback(config);
                    return;
                }

                key = keys[i];
                value = config[key];

                Ext.factoryConfig(value, fn);
            }

            factory();
            return;
        }

        callback(config);
    },

    
    factory: function(config, classReference, instance, aliasNamespace) {
        var manager = Ext.ClassManager;

        
        
        if (!config || config.isInstance) {
            if (instance && instance !== config) {
                instance.destroy();
            }

            return config;
        }

        if (aliasNamespace) {
             
            if (typeof config == 'string') {
                return manager.instantiateByAlias(aliasNamespace + '.' + config);
            }
            
            else if ('type' in config) {
                return manager.instantiateByAlias(aliasNamespace + '.' + config.type, config);
            }
        }
        else if (typeof config == 'string') {
            return Ext.getCmp(config);
        }

        if (config === true) {
            return manager.instantiate(classReference);
        }

        if (!Ext.isObject(config)) {
            Ext.Logger.error("Invalid config, must be a valid config object");
        }

        if ('xtype' in config) {
            return manager.instantiateByAlias('widget.' + config.xtype, config);
        }

        if ('xclass' in config) {
            return manager.instantiate(config.xclass, config);
        }

        if (instance) {
            return instance.setConfig(config);
        }

        return manager.instantiate(classReference, config);
    },

    
    deprecateClassMember: function(cls, oldName, newName, message) {
        return this.deprecateProperty(cls.prototype, oldName, newName, message);
    },

   
    deprecateClassMembers: function(cls, members) {
       var prototype = cls.prototype,
           oldName, newName;

       for (oldName in members) {
           if (members.hasOwnProperty(oldName)) {
               newName = members[oldName];

               this.deprecateProperty(prototype, oldName, newName);
           }
       }
    },

    
    deprecateProperty: function(object, oldName, newName, message) {
        if (!message) {
            message = "'" + oldName + "' is deprecated, please use '" + newName + "' instead";
        }

        function getter() {
            Ext.Logger.deprecate(message, 1);

            return this[newName];
        }

        function setter(value) {
            Ext.Logger.deprecate(message, 1);
            this[newName] = value;
        }

        if ('defineProperty' in Object) {
            Object.defineProperty(object, oldName, {
                get: getter,
                set: setter
            });
        }
        else {
            object.__defineGetter__(oldName, getter);
            object.__defineSetter__(oldName, setter);
        }
    },

    
    deprecateMethod: function(object, name, method, message) {
        object[name] = function() {
            Ext.Logger.deprecate(message, 2);
            return method.apply(this, arguments);
        };
    },

    
    deprecateClassMethod: function(cls, name, method, message) {
        var isLateBinding = typeof method == 'string',
            member;

        if (!message) {
            if (isLateBinding) {
                message = "'" + name + "()' is deprecated, please use '" + method + "()' instead";
            }
            else {
                message = "'" + name + "()' is deprecated.";
            }
        }

        if (isLateBinding) {
            member = function() {
                Ext.Logger.deprecate(message, this);

                return this[method].apply(this, arguments);
            };
        }
        else {
            member = function() {
                Ext.Logger.deprecate(message, this);

                return method.apply(this, arguments);
            };
        }

        cls.addMember(name, member);
    },

    
    deprecateClassConfigDirectAccess: function(cls, data) {
        var prototype = cls.prototype,
            config = prototype.config;

        if (config) {
            Ext.Object.each(config, function(key) {
                if (!(key in prototype)) {
                    var capitalizedKey = Ext.String.capitalize(key),
                        getterName = 'get' + capitalizedKey,
                        setterName = 'set' + capitalizedKey;

                    function getter() {
                        Ext.Logger.deprecate("Access to config '" + key + "' directly is deprecated, please use " + getterName + "() instead", 1);

                        var fn = this[getterName];

                        if (fn === getter.caller) {
                            throw new Error("Infinite recursion detected: accessing '" + key + "' config inside of " + getterName + "()");
                        }

                        return fn.apply(this, arguments);
                    }

                    function setter() {
                        Ext.Logger.deprecate("Setting config '" + key + "' value directly is deprecated, please use " + setterName + "() instead", 1);

                        var fn = this[setterName];

                        if (fn === setter.caller) {
                            throw new Error("Infinite recursion detected: setting '" + key + "' config inside of " + setterName + "()");
                        }

                        return fn.apply(this, arguments);
                    }

                    if ('defineProperty' in Object) {
                        Object.defineProperty(object, oldName, {
                            get: getter,
                            set: setter
                        });
                    }
                    else {
                        object.__defineGetter__(oldName, getter);
                        object.__defineSetter__(oldName, setter);
                    }
                    Object.defineProperty(prototype, key, {
                        get: function getter() {
                            Ext.Logger.deprecate("Access to config '" + key + "' directly is deprecated, please use " + getterName + "() instead", 1);

                            var fn = this[getterName];

                            if (fn === getter.caller) {
                                throw new Error("Infinite recursion detected: accessing '" + key + "' config inside of " + getterName + "()");
                            }

                            return fn.apply(this, arguments);
                        },

                        set: function setter() {
                            Ext.Logger.deprecate("Setting config '" + key + "' value directly is deprecated, please use " + setterName + "() instead", 1);

                            var fn = this[setterName];

                            if (fn === setter.caller) {
                                throw new Error("Infinite recursion detected: setting '" + key + "' config inside of " + setterName + "()");
                            }

                            return fn.apply(this, arguments);
                        }
                    });

                }

                if (data && key in data && key in config) {
                    throw new Error("["+Ext.getClassName(cls)+"] Defining class property: '" + key + "' with an already existing config item with the same name. Move it inside the 'config' object instead.");
                }
            });
        }
    },

    
    isReady : false,

    
    readyListeners: [],

    
    triggerReady: function() {
        var listeners = Ext.readyListeners,
            i, ln, listener;

        if (!Ext.isReady) {
            Ext.isReady = true;

            
            
            Ext.Function.defer(function() {
                for (i = 0, ln = listeners.length; i < ln; i++) {
                    listener = listeners[i];
                    listener.fn.call(listener.scope);
                }
                delete Ext.readyListeners;
            }, 1);
        }
    },

    
    onDocumentReady: function(fn, scope) {
        if (Ext.isReady) {
            fn.call(scope);
        }
        else {
            var triggerFn = Ext.triggerReady;

            Ext.readyListeners.push({
                fn: fn,
                scope: scope
            });

            if (Ext.browser.is.PhoneGap) {
                if (!Ext.readyListenerAttached) {
                    Ext.readyListenerAttached = true;
                    document.addEventListener('deviceready', triggerFn, false);
                }
            }
            else {
                if (document.readyState.match(/interactive|complete|loaded/) !== null) {
                    triggerFn();
                }
                else if (!Ext.readyListenerAttached) {
                    Ext.readyListenerAttached = true;
                    window.addEventListener('DOMContentLoaded', triggerFn, false);
                }
            }
        }
    },

    
    callback: function(callback, scope, args, delay) {
        if (Ext.isFunction(callback)) {
            args = args || [];
            scope = scope || window;
            if (delay) {
                Ext.defer(callback, delay, scope, args);
            } else {
                callback.apply(scope, args);
            }
        }
    }
});


Ext.deprecateMethod(Ext.Function, 'createDelegate', Ext.Function.bind, "Ext.createDelegate() is deprecated, please use Ext.Function.bind() instead");


Ext.deprecateMethod(Ext, 'createInterceptor', Ext.Function.createInterceptor, "Ext.createInterceptor() is deprecated, " +
    "please use Ext.Function.createInterceptor() instead");


Ext.define('Ext.env.Browser', {
    requires: ['Ext.Version'],

    statics: {
        browserNames: {
            ie: 'IE',
            firefox: 'Firefox',
            safari: 'Safari',
            chrome: 'Chrome',
            opera: 'Opera',
            dolfin: 'Dolfin',
            webosbrowser: 'webOSBrowser',
            other: 'Other'
        },
        engineNames: {
            webkit: 'WebKit',
            gecko: 'Gecko',
            presto: 'Presto',
            trident: 'Trident',
            other: 'Other'
        },
        enginePrefixes: {
            webkit: 'AppleWebKit/',
            gecko: 'Gecko/',
            presto: 'Presto/',
            trident: 'Trident/'
        },
        browserPrefixes: {
            ie: 'MSIE ',
            firefox: 'Firefox/',
            chrome: 'Chrome/',
            safari: 'Version/',
            opera: 'Opera/',
            dolfin: 'Dolfin/',
            webosbrowser: 'wOSBrowser/'
        }
    },

    styleDashPrefixes: {
        WebKit: '-webkit-',
        Gecko: '-moz-',
        Trident: '-ms-',
        Presto: '-o-',
        Other: ''
    },

    stylePrefixes: {
        WebKit: 'Webkit',
        Gecko: 'Moz',
        Trident: 'ms',
        Presto: 'O',
        Other: ''
    },

    propertyPrefixes: {
        WebKit: 'webkit',
        Gecko: 'moz',
        Trident: 'ms',
        Presto: 'o',
        Other: ''
    },

    

    
    is: Ext.emptyFn,

    
    name: null,

    
    version: null,

    
    engineName: null,

    
    engineVersion: null,

    setFlag: function(name, value) {
        if (typeof value == 'undefined') {
            value = true;
        }

        this.is[name] = value;
        this.is[name.toLowerCase()] = value;

        return this;
    },

    constructor: function(userAgent) {
        this.userAgent = userAgent;

        is = this.is = function(name) {
            return is[name] === true;
        };

        var statics = this.statics(),
            browserMatch = userAgent.match(new RegExp('((?:' + Ext.Object.getValues(statics.browserPrefixes).join(')|(?:') + '))([\\w\\._]+)')),
            engineMatch = userAgent.match(new RegExp('((?:' + Ext.Object.getValues(statics.enginePrefixes).join(')|(?:') + '))([\\w\\._]+)')),
            browserNames = statics.browserNames,
            browserName = browserNames.other,
            engineNames = statics.engineNames,
            engineName = engineNames.other,
            browserVersion = '',
            engineVersion = '',
            isWebView = false,
            is, i, name;

        if (browserMatch) {
            browserName = browserNames[Ext.Object.getKey(statics.browserPrefixes, browserMatch[1])];

            browserVersion = new Ext.Version(browserMatch[2]);
        }

        if (engineMatch) {
            engineName = engineNames[Ext.Object.getKey(statics.enginePrefixes, engineMatch[1])];
            engineVersion = new Ext.Version(engineMatch[2]);
        }

        Ext.apply(this, {
            engineName: engineName,
            engineVersion: engineVersion,
            name: browserName,
            version: browserVersion
        });

        this.setFlag(browserName);

        if (browserVersion) {
            this.setFlag(browserName + (browserVersion.getMajor() || ''));
            this.setFlag(browserName + browserVersion.getShortVersion());
        }

        for (i in browserNames) {
            if (browserNames.hasOwnProperty(i)) {
                name = browserNames[i];

                this.setFlag(name, browserName === name);
            }
        }

        this.setFlag(name);

        if (engineVersion) {
            this.setFlag(engineName + (engineVersion.getMajor() || ''));
            this.setFlag(engineName + engineVersion.getShortVersion());
        }

        for (i in engineNames) {
            if (engineNames.hasOwnProperty(i)) {
                name = engineNames[i];

                this.setFlag(name, engineName === name);
            }
        }

        this.setFlag('Standalone', !!navigator.standalone);

        if (typeof window.PhoneGap != 'undefined') {
            isWebView = true;
            this.setFlag('PhoneGap');
        }
        else if (!!window.isNK) {
            isWebView = true;
            this.setFlag('Sencha');
        }

        
        this.setFlag('WebView', isWebView);

        this.isStrict = document.compatMode == "CSS1Compat";

        this.isSecure = /^https/i.test(window.location.protocol);

        return this;
    },

    getStyleDashPrefix: function() {
        return this.styleDashPrefixes[this.engineName];
    },

    getStylePrefix: function() {
        return this.stylePrefixes[this.engineName];
    },

    getVendorProperyName: function(name) {
        var prefix = this.propertyPrefixes[this.engineName];

        if (prefix.length > 0) {
            return prefix + Ext.String.capitalize(name);
        }

        return name;
    }

}, function() {
    var browserEnv = Ext.browser = new this(Ext.global.navigator.userAgent);

    var flags = browserEnv.is,
        name;

    if (!Ext.is) {
        Ext.is = {};
    }

    for (name in flags) {
        if (flags.hasOwnProperty(name)) {
            Ext.deprecateProperty(Ext.is, name, flags[name], "Ext.is." + name + " is deprecated, please use Ext.browser.is." + name + " instead");
        }
    }

    Ext.deprecateProperty(Ext, 'isSecure', browserEnv.isSecure, "Ext.isSecure is deprecated, please use Ext.browser.isSecure instead");

    Ext.deprecateProperty(Ext, 'isStrict', browserEnv.isStrict, "Ext.isStrict is deprecated, please use Ext.browser.isStrict instead");

    Ext.deprecateProperty(Ext, 'userAgent', browserEnv.userAgent, "Ext.userAgent is deprecated, please use Ext.browser.userAgent instead");
});



Ext.define('Ext.env.OS', {

    requires: ['Ext.Version'],

    statics: {
        names: {
            ios: 'iOS',
            android: 'Android',
            webos: 'webOS',
            blackberry: 'BlackBerry',
            rimTablet: 'RIMTablet',
            mac: 'MacOS',
            win: 'Windows',
            linux: 'Linux',
            bada: 'Bada',
            other: 'Other'
        },
        prefixes: {
            ios: 'i(?:Pad|Phone|Pod)(?:.*)CPU(?: iPhone)? OS ',
            android: 'Android ',
            blackberry: 'BlackBerry(?:.*)Version\/',
            rimTablet: 'RIM Tablet OS ',
            webos: '(?:webOS|hpwOS)\/',
            bada: 'Bada\/'
        }
    },

    
    is: Ext.emptyFn,

    
    name: null,

    
    version: null,

    setFlag: function(name, value) {
        if (typeof value == 'undefined') {
            value = true;
        }

        this.is[name] = value;
        this.is[name.toLowerCase()] = value;

        return this;
    },

    constructor: function(userAgent, platform) {
        var statics = this.statics(),
            names = statics.names,
            prefixes = statics.prefixes,
            name,
            version = '',
            i, prefix, match, item, is;

        is = this.is = function(name) {
            return this.is[name] === true;
        };

        for (i in prefixes) {
            if (prefixes.hasOwnProperty(i)) {
                prefix = prefixes[i];

                match = userAgent.match(new RegExp('(?:'+prefix+')([^\\s;]+)'));

                if (match) {
                    name = names[i];
                    version = new Ext.Version(match[match.length - 1]);
                    break;
                }
            }
        }

        if (!name) {
            name = names[(userAgent.toLowerCase().match(/mac|win|linux/) || ['other'])[0]];
            version = new Ext.Version('');
        }

        Ext.apply(this, {
            name: name,
            version: version
        });

        if (platform) {
            this.setFlag(platform);
        }

        this.setFlag(name);

        if (version) {
            this.setFlag(name + (version.getMajor() || ''));
            this.setFlag(name + version.getShortVersion());
        }

        for (i in names) {
            if (names.hasOwnProperty(i)) {
                item = names[i];

                if (!is.hasOwnProperty(name)) {
                    this.setFlag(item, (name === item));
                }
            }
        }

        return this;
    }

}, function() {
    
    var navigator = Ext.global.navigator,
        osEnv, osName, osVersion, deviceType;

    this.override('constructor', function() {
        this.callOverridden(arguments);

        var is = this.is;

        if (is.MacOS) {
            Ext.deprecateProperty(is, 'Mac', true, "Ext.is.Mac is deprecated, please use Ext.os.is.MacOS instead");
            Ext.deprecateProperty(is, 'mac', true, "Ext.is.Mac is deprecated, please use Ext.os.is.MacOS instead");
        }

        if (is.BlackBerry) {
            Ext.deprecateProperty(is, 'Blackberry', true, "Ext.is.Blackberry is deprecated, please use Ext.os.is.BlackBerry instead");
        }

        return this;
    });

    Ext.os = osEnv = new this(navigator.userAgent, navigator.platform);

    osName = osEnv.name;
    osVersion = osEnv.version;

    var flags = Ext.os.is,
        search = window.location.search.match(/deviceType=(Tablet|Phone)/),
        name;

    if (!Ext.is) {
        Ext.is = {};
    }

    for (name in flags) {
        if (flags.hasOwnProperty(name)) {
            Ext.deprecateProperty(Ext.is, name, flags[name], "Ext.is." + name + " is deprecated, please use Ext.os.is." + name + " instead");
        }
    }

    
    
    if (search && search[1]) {
        deviceType = search[1];
    } else {
        
        if (/Windows|Linux|MacOS/.test(osName)) {
            deviceType = 'Desktop';
        }
        else if (osEnv.is.iPad || osEnv.is.Android3) {
            deviceType = 'Tablet';
        }
        else {
            deviceType = 'Phone';
        }
    }

    osEnv.setFlag(deviceType, true);
    osEnv.deviceType = deviceType;

});


Ext.define('Ext.env.Feature', {

    requires: ['Ext.env.Browser', 'Ext.env.OS'],

    constructor: function() {
        this.testElements = {};

        this.has = function(name) {
            return !!this.has[name];
        };

        return this;
    },

    getTestElement: function(tag, createNew) {
        if (tag === undefined) {
            tag = 'div';
        }
        else if (typeof tag !== 'string') {
            return tag;
        }

        if (createNew) {
            return document.createElement(tag);
        }

        if (!this.testElements[tag]) {
            this.testElements[tag] = document.createElement(tag);
        }

        return this.testElements[tag];
    },

    isStyleSupported: function(name, tag) {
        var elementStyle = this.getTestElement(tag).style,
            cName = Ext.String.capitalize(name);

        if (typeof elementStyle[name] !== 'undefined'
            || typeof elementStyle[Ext.browser.getStylePrefix(name) + cName] !== 'undefined') {
            return true;
        }

        return false;
    },

    isEventSupported: function(name, tag) {
        if (tag === undefined) {
            tag = window;
        }

        var element = this.getTestElement(tag),
            eventName = 'on' + name.toLowerCase(),
            isSupported = false;

        isSupported = (eventName in element);

        if (!isSupported) {
            if (element.setAttribute && element.removeAttribute) {
                element.setAttribute(eventName, '');
                isSupported = typeof element[eventName] === 'function';

                if (typeof element[eventName] !== 'undefined') {
                    element[eventName] = undefined;
                }

                element.removeAttribute(eventName);
            }
        }

        return isSupported;
    },

    getSupportedPropertyName: function(object, name) {
        var vendorName = Ext.browser.getVendorProperyName(name);

        if (vendorName in object) {
            return vendorName;
        }
        else if (name in object) {
            return name;
        }

        return null;
    },

    registerTest: Ext.Function.flexSetter(function(name, fn) {
        this.has[name] = fn.call(this);

        return this;
    })

}, function() {

    Ext.feature = new this;

    var has = Ext.feature.has;

    Ext.feature.registerTest({
        Canvas: function() {
            var element = this.getTestElement('canvas');
            return !!(element && element.getContext && element.getContext('2d'));
        },
        Svg: function() {
            var doc = document;

            return !!(doc.createElementNS && !!doc.createElementNS("http:/" + "/www.w3.org/2000/svg", "svg").createSVGRect);
        },
        Vml: function() {
            var element = this.getTestElement(),
                ret = false;

            element.innerHTML = "<!--[if vml]><br><![endif]-->";
            ret = (element.childNodes.length === 1);
            element.innerHTML = "";

            return ret;
        },
        Touch: function() {
            return this.isEventSupported('touchstart') && !(Ext.os && Ext.os.name.match(/Windows|MacOSX|Linux/));
        },
        Orientation: function() {
            return ('orientation' in window) && this.isEventSupported('orientationchange');
        },
        OrientationChange: function() {
            return this.isEventSupported('orientationchange');
        },
        DeviceMotion: function() {
            return this.isEventSupported('devicemotion');
        },
        Geolocation: function() {
            return 'geolocation' in window.navigator;
        },
        SqlDatabase: function() {
            return 'openDatabase' in window;
        },
        WebSockets: function() {
            return 'WebSocket' in window;
        },
        History: function() {
            return ('history' in window && 'pushState' in window.history);
        },
        CssTransforms: function() {
            return this.isStyleSupported('transform');
        },
        Css3dTransforms: function() {
            
            return this.has('CssTransforms') && this.isStyleSupported('perspective') && !Ext.os.is.Android2;
        },
        CssAnimations: function() {
            return this.isStyleSupported('animationName');
        },
        CssTransitions: function() {
            return this.isStyleSupported('transitionProperty');
        },
        Audio: function() {
            return !!this.getTestElement('audio').canPlayType;
        },
        Video: function() {
            return !!this.getTestElement('video').canPlayType;
        }
    });

    
    
    Ext.deprecateProperty(has, 'Transitions', has.CssTransitions,
                          "Ext.supports.Transitions is deprecated, please use Ext.feature.has.CssTransitions instead");

    Ext.deprecateProperty(has, 'SVG', has.Svg,
                          "Ext.supports.SVG is deprecated, please use Ext.feature.has.Svg instead");

    Ext.deprecateProperty(has, 'VML', has.Vml,
                          "Ext.supports.VML is deprecated, please use Ext.feature.has.Vml instead");

    Ext.deprecateProperty(has, 'AudioTag', has.Audio,
                          "Ext.supports.AudioTag is deprecated, please use Ext.feature.has.Audio instead");

    Ext.deprecateProperty(has, 'GeoLocation', has.Geolocation,
                          "Ext.supports.GeoLocation is deprecated, please use Ext.feature.has.Geolocation instead");

    var name;

    if (!Ext.supports) {
        Ext.supports = {};
    }

    for (name in has) {
        if (has.hasOwnProperty(name)) {
            Ext.deprecateProperty(Ext.supports, name, has[name], "Ext.supports." + name + " is deprecated, please use Ext.feature.has." + name + " instead");
        }
    }
});


Ext.define('Ext.dom.AbstractQuery', {
    
    select: function(q, root) {
        var results = [],
            nodes,
            i,
            j,
            qlen,
            nlen;

        root = root || document;

        if (typeof root == 'string') {
            root = document.getElementById(root);
        }

        q = q.split(",");

        for (i = 0,qlen = q.length; i < qlen; i++) {
            if (typeof q[i] == 'string') {
                
                
                if (typeof q[i][0] == '@') {
                    nodes = root.getAttributeNode(q[i].substring(1));
                    results.push(nodes);
                } else {
                    nodes = root.querySelectorAll(q[i]);

                    for (j = 0,nlen = nodes.length; j < nlen; j++) {
                        results.push(nodes[j]);
                    }
                }
            }
        }

        return results;
    },    

    
    selectNode: function(q, root) {
        return this.select(q, root)[0];
    },

    
    is: function(el, q) {
        if (typeof el == "string") {
            el = document.getElementById(el);
        }
        return this.select(q).indexOf(el) !== -1;
    }

});


Ext.define('Ext.dom.AbstractHelper', {
    emptyTags : /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i,
    confRe : /tag|children|cn|html$/i,
    endRe : /end/i,

    attribXlat: { cls : 'class', htmlFor : 'for' },

    generateMarkup: function(spec, buffer) {
        var me = this,
            attr,
            val,
            key,
            cn,
            tag,
            i;

        if (typeof spec == "string") {
            buffer.push(spec);
        } else if (Ext.isArray(spec)) {
            for (i=0; i < spec.length; i++) {
                if (spec[i]) {
                    me.generateMarkup(spec[i], buffer);
                }
            }
        } else {
            tag = spec.tag || 'div';
            buffer.push('<', tag);

            for (attr in spec) {
                if (spec.hasOwnProperty(attr)) {
                    val = spec[attr];
                    if (!me.confRe.test(attr)) {
                        if (typeof val == "object") {
                            buffer.push(' ', attr, '="');
                            for (key in val) {
                                if (val.hasOwnProperty(key)) {
                                    buffer.push(key, ':', val[key], ';');
                                }
                            }
                            buffer.push('"');
                        } else {
                            buffer.push(' ', me.attribXlat[attr] || attr, '="', val, '"');
                        }
                    }
                }
            }

            
            if (me.emptyTags.test(tag)) {
                buffer.push('/>');
            } else {
                buffer.push('>');
                if ((cn = spec.children || spec.cn)) {
                    me.generateMarkup(cn, buffer);
                } else if (spec.html) {
                    buffer.push(spec.html);
                }
                buffer.push('</', tag, '>');
            }
        }

        return buffer;
    },

    
    markup: function(spec) {
        if (typeof spec == "string") {
            return spec;
        }

        var buf = this.generateMarkup(spec, []);
        return buf.join('');
    },

    
    applyStyles: function(el, styles) {
        if (styles) {
            var i = 0,
                len,
                style;

            el = Ext.fly(el);
            if (typeof styles == 'function') {
                styles = styles.call();
            }
            if (typeof styles == 'string'){
                styles = Ext.util.Format.trim(styles).split(/\s*(?::|;)\s*/);
                for(len = styles.length; i < len;){
                    el.setStyle(styles[i++], styles[i++]);
                }
            } else if (Ext.isObject(styles)) {
                el.setStyle(styles);
            }
        }
    },

    
    insertHtml: function(where, el, html) {
        var hash = {},
            hashVal,
            setStart,
            range,
            frag,
            rangeEl,
            rs;

        where = where.toLowerCase();

        
        hash['beforebegin'] = ['BeforeBegin', 'previousSibling'];
        hash['afterend'] = ['AfterEnd', 'nextSibling'];

        range = el.ownerDocument.createRange();
        setStart = 'setStart' + (this.endRe.test(where) ? 'After' : 'Before');
        if (hash[where]) {
            range[setStart](el);
            frag = range.createContextualFragment(html);
            el.parentNode.insertBefore(frag, where == 'beforebegin' ? el : el.nextSibling);
            return el[(where == 'beforebegin' ? 'previous' : 'next') + 'Sibling'];
        }
        else {
            rangeEl = (where == 'afterbegin' ? 'first' : 'last') + 'Child';
            if (el.firstChild) {
                range[setStart](el[rangeEl]);
                frag = range.createContextualFragment(html);
                if (where == 'afterbegin') {
                    el.insertBefore(frag, el.firstChild);
                }
                else {
                    el.appendChild(frag);
                }
            }
            else {
                el.innerHTML = html;
            }
            return el[rangeEl];
        }

        throw 'Illegal insertion point -> "' + where + '"';
    },

    
    insertBefore: function(el, o, returnElement) {
        return this.doInsert(el, o, returnElement, 'beforebegin');
    },

    
    insertAfter: function(el, o, returnElement) {
        return this.doInsert(el, o, returnElement, 'afterend', 'nextSibling');
    },

    
    insertFirst: function(el, o, returnElement) {
        return this.doInsert(el, o, returnElement, 'afterbegin', 'firstChild');
    },

    
    append: function(el, o, returnElement) {
        return this.doInsert(el, o, returnElement, 'beforeend', '', true);
    },

    
    overwrite: function(el, o, returnElement) {
        el = Ext.getDom(el);
        el.innerHTML = this.markup(o);
        return returnElement ? Ext.get(el.firstChild) : el.firstChild;
    },

    doInsert: function(el, o, returnElement, pos, sibling, append) {
        var newNode = this.insertHtml(pos, Ext.getDom(el), this.markup(o));
        return returnElement ? Ext.get(newNode, true) : newNode;
    }

});


(function() {

var document = window.document;

if (!Ext.cache){
    Ext.cache = {};
}

Ext.define('Ext.dom.AbstractElement', {

    inheritableStatics: {

        
        get: function(el) {
            var me = this,
                El = Ext.dom.Element,
                extEl,
                dom,
                id;

            if (!el) {
                return null;
            }

            if (typeof el == "string") { 
                if (!(dom = document.getElementById(el))) {
                    return null;
                }

                if (Ext.cache[el] && Ext.cache[el].el) {
                    extEl = Ext.cache[el].el;
                    extEl.dom = dom;
                } else {
                    extEl = me.addToCache(new El(dom));
                }
                return extEl;
            } else if (el.tagName) { 
                if (!(id = el.id)) {
                    id = Ext.id(el);
                }
                if (Ext.cache[id] && Ext.cache[id].el) {
                    extEl = Ext.cache[id].el;
                    extEl.dom = el;
                } else {
                    extEl = me.addToCache(new El(el));
                }
                return extEl;
            } else if (el instanceof me) {
                if (el != me.docEl) {
                    
                    
                    el.dom = document.getElementById(el.id) || el.dom;
                }
                return el;
            } else if (el.isComposite) {
                return el;
            } else if (Ext.isArray(el)) {
                return me.select(el);
            } else if (el == document) {
                
                if (!me.docEl) {
                    me.docEl = Ext.Object.chain(El.prototype);
                    me.docEl.dom = document;
                    document.documentElement.id = me.docEl.id = Ext.id(document);
                }

                return me.docEl;
            }

            return null;
        },

        addToCache: function(el, id) {
            if (el) {
                id = id || el.id;
                Ext.cache[id] = {
                    el: el,
                    data: {},
                    events: {}
                };
            }
            return el;
        },

        
        data: function(el, key, value) {
            el = this.get(el);
            if (!el) {
                return null;
            }
            var c = Ext.cache[el.id].data;
            if (!c) {
                c = Ext.cache[el.id].data = {};
            }
            if (arguments.length == 2) {
                return c[key];
            } else {
                return (c[key] = value);
            }
        },

        addMethods: function() {
            this.override.apply(this, arguments);
        },

        
        VISIBILITY: 1,

        
        DISPLAY: 2,

        
        OFFSETS: 3
    },

    constructor: function(element, forceNew) {
        var dom = typeof element == 'string'
                ? document.getElementById(element)
                : element,
            id;

        if (!dom) {
            return null;
        }

        id = dom.id;
        if (!forceNew && id && Ext.cache[id]) {
            
            return Ext.cache[id].el;
        }

        
        this.dom = dom;

        
        this.id = id || Ext.id(dom);
    },

    attach: function (dom) {
        this.dom = dom;
        this.id = dom.id;
        return this;
    },

    
    set: function(o, useSet) {
         var el = this.dom,
             attr,
             value;

         for (attr in o) {
             if (o.hasOwnProperty(attr)) {
                 value = o[attr];
                 if (attr == 'style') {
                     this.applyStyles(value);
                 }
                 else if (attr == 'cls') {
                     el.className = value;
                 }
                 else if (useSet !== false) {
                     if (value === undefined) {
                         el.removeAttribute(attr);
                     } else {
                        el.setAttribute(attr, value);
                     }
                 }
                 else {
                     el[attr] = value;
                 }
             }
         }
         return this;
     },

    
    defaultUnit: "px",

    
    is: function(simpleSelector) {
        return Ext.DomQuery.is(this.dom, simpleSelector);
    },

    
    getValue: function(asNumber) {
        var val = this.dom.value;
        return asNumber ? parseInt(val, 10) : val;
    },

    
    remove: function() {
        var me = this,
        dom = me.dom;

        if (dom) {
            delete me.dom;
            Ext.removeNode(dom);
        }
    },

    
    contains: function(el) {
        if (!el) {
            return false;
        }

        var me = this,
            dom = el.dom || el;

        
        return (dom === me.dom) || Ext.dom.AbstractElement.isAncestor(me.dom, dom);
    },

    
    getAttribute: function(name, ns) {
        var dom = this.dom;
        return dom.getAttributeNS(ns, name) || dom.getAttribute(ns + ":" + name) || dom.getAttribute(name) || dom[name];
    },

    
    update: function(html) {
        if (this.dom) {
            this.dom.innerHTML = html;
        }
        return this;
    },


    
    setHTML: function(html) {
        if(this.dom) {
            this.dom.innerHTML = html;
        }
        return this;
    },

    
    getHTML: function() {
        return this.dom ? this.dom.innerHTML : '';
    },

    
    hide: function() {
        this.setVisible(false);
        return this;
    },

    
    show: function() {
        this.setVisible(true);
        return this;
    },

    
     setVisible: function(visible, animate) {
        var me = this,
            statics = me.self,
            mode = me.getVisibilityMode();

        switch (mode) {
            case statics.VISIBILITY:
                me.removeCls(['x-hidden-display', 'x-hidden-offsets']);
                me[visible ? 'removeCls' : 'addCls']('x-hidden-visibility');
            break;

            case statics.DISPLAY:
                me.removeCls(['x-hidden-visibility', 'x-hidden-offsets']);
                me[visible ? 'removeCls' : 'addCls']('x-hidden-display');
            break;

            case statics.OFFSETS:
                me.removeCls(['x-hidden-visibility', 'x-hidden-display']);
                me[visible ? 'removeCls' : 'addCls']('x-hidden-offsets');
            break;
        }

        return me;
    },

    getVisibilityMode: function() {
        var statics = this.self,
            dom = this.dom,
            mode = statics.data(dom, 'visibilityMode');

        if (mode === undefined) {
            statics.data(dom, 'visibilityMode', mode = statics.DISPLAY);
        }

        return mode;
    },

    
    setVisibilityMode: function(mode) {
        this.self.data(this.dom, 'visibilityMode', mode);
        return this;
    }
}, function() {
    var AbstractElement = this;

    
    Ext.get = function() {
        var El = Ext.dom.Element;
        return El.get.apply(El, arguments);
    };

    this.addStatics({
        Fly: new Ext.Class({
            extend: AbstractElement,

            constructor: function(dom) {
                this.dom = dom;
            }
        }),

        _flyweights: {},

        
        fly: function(el, named) {
            var ret = null,
                _flyweights = AbstractElement._flyweights;

            named = named || '_global';

            el = Ext.getDom(el);

            if (el) {
                ret = _flyweights[named] || (_flyweights[named] = new AbstractElement.Fly());
                ret.dom = el;
            }

            return ret;
        }
    });

    
    Ext.fly = function() {
        return AbstractElement.fly.apply(AbstractElement, arguments);
    };

    (function (proto) {
        
        proto.destroy = proto.remove;

        
        proto.getById = Ext.get;
    })(this.prototype);
});

})();


Ext.dom.AbstractElement.addInheritableStatics({
    unitRe: /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i,
    camelRe: /(-[a-z])/gi,
    cssRe: /([a-z0-9-]+)\s*:\s*([^;\s]+(?:\s*[^;\s]+)*);?/gi,
    opacityRe: /alpha\(opacity=(.*)\)/i,
    propertyCache: {},
    defaultUnit : "px",
    borders: {l: 'border-left-width', r: 'border-right-width', t: 'border-top-width', b: 'border-bottom-width'},
    paddings: {l: 'padding-left', r: 'padding-right', t: 'padding-top', b: 'padding-bottom'},
    margins: {l: 'margin-left', r: 'margin-right', t: 'margin-top', b: 'margin-bottom'},

    
    addUnits: function(size, units) {

        
        if (Ext.isNumber(size)) {
            return size + (units || this.defaultUnit || 'px');
        }

        
        if (size === "" || size == "auto" || size === undefined || size === null) {
            return size || '';
        }

        
        if (!this.unitRe.test(size)) {
            if (Ext.isDefined(Ext.global.console)) {
                Ext.global.console.warn("Warning, size detected as NaN on Element.addUnits.");
            }
            return size || '';
        }
        return size;
    },

    
    isAncestor: function(p, c) {
        var ret = false;

        p = Ext.getDom(p);
        c = Ext.getDom(c);
        if (p && c) {
            if (p.contains) {
                return p.contains(c);
            } else if (p.compareDocumentPosition) {
                return !!(p.compareDocumentPosition(c) & 16);
            } else {
                while ((c = c.parentNode)) {
                    ret = c == p || ret;
                }
            }
        }
        return ret;
    },

    
    parseBox: function(box) {
        if (typeof box != 'string') {
            box = box.toString();
        }
        var parts  = box.split(' '),
            ln = parts.length;

        if (ln == 1) {
            parts[1] = parts[2] = parts[3] = parts[0];
        }
        else if (ln == 2) {
            parts[2] = parts[0];
            parts[3] = parts[1];
        }
        else if (ln == 3) {
            parts[3] = parts[1];
        }

        return {
            top   :parseFloat(parts[0]) || 0,
            right :parseFloat(parts[1]) || 0,
            bottom:parseFloat(parts[2]) || 0,
            left  :parseFloat(parts[3]) || 0
        };
    },

    
    unitizeBox: function(box, units) {
        var A = this.addUnits,
            B = this.parseBox(box);

        return A(B.top, units) + ' ' +
               A(B.right, units) + ' ' +
               A(B.bottom, units) + ' ' +
               A(B.left, units);

    },

    
    camelReplaceFn: function(m, a) {
        return a.charAt(1).toUpperCase();
    },

    
    normalize: function(prop) {
        
        if (prop == 'float') {
            prop = Ext.supports.Float ? 'cssFloat' : 'styleFloat';
        }
        return this.propertyCache[prop] || (this.propertyCache[prop] = prop.replace(this.camelRe, this.camelReplaceFn));
    },

    
    getDocumentHeight: function() {
        return Math.max(!Ext.isStrict ? document.body.scrollHeight : document.documentElement.scrollHeight, this.getViewportHeight());
    },

    
    getDocumentWidth: function() {
        return Math.max(!Ext.isStrict ? document.body.scrollWidth : document.documentElement.scrollWidth, this.getViewportWidth());
    },

    
    getViewportHeight: function(){
        return window.innerHeight;
    },

    
    getViewportWidth: function() {
        return window.innerWidth;
    },

    
    getViewSize: function() {
        return {
            width: window.innerWidth,
            height: window.innerHeight
        };
    },

    
    getOrientation: function() {
        if (Ext.supports.OrientationChange) {
            return (window.orientation == 0) ? 'portrait' : 'landscape';
        }

        return (window.innerHeight > window.innerWidth) ? 'portrait' : 'landscape';
    },

    
    fromPoint: function(x, y) {
        return Ext.get(document.elementFromPoint(x, y));
    },

    
    parseStyles: function(styles){
        var out = {},
            cssRe = this.cssRe,
            matches;

        if (styles) {
            
            
            
            
            cssRe.lastIndex = 0;
            while ((matches = cssRe.exec(styles))) {
                out[matches[1]] = matches[2];
            }
        }
        return out;
    }
});


(function(){
    var doc = document,
        AbstractElement = Ext.dom.AbstractElement,
        activeElement = null,
        isCSS1 = doc.compatMode == "CSS1Compat",
        flyInstance,
        fly = function (el) {
            if (!flyInstance) {
                flyInstance = new AbstractElement.Fly();
            }
            flyInstance.attach(el);
            return flyInstance;
        };

    
    
    
    if (!('activeElement' in doc) && doc.addEventListener) {
        doc.addEventListener('focus',
            function (ev) {
                if (ev && ev.target) {
                    activeElement = (ev.target == doc) ? null : ev.target;
                }
            }, true);
    }

    
    function makeSelectionRestoreFn (activeEl, start, end) {
        return function () {
            activeEl.selectionStart = start;
            activeEl.selectionEnd = end;
        };
    }

    AbstractElement.addInheritableStatics({
        
        getActiveElement: function () {
            return doc.activeElement || activeElement;
        },

        
        getRightMarginFixCleaner: function (target) {
            var supports = Ext.supports,
                hasInputBug = supports.DisplayChangeInputSelectionBug,
                hasTextAreaBug = supports.DisplayChangeTextAreaSelectionBug;

            if (hasInputBug || hasTextAreaBug) {
                var activeEl = doc.activeElement || activeElement, 
                    tag = activeEl && activeEl.tagName,
                    start,
                    end;

                if ((hasTextAreaBug && tag == 'TEXTAREA') ||
                    (hasInputBug && tag == 'INPUT' && activeEl.type == 'text')) {
                    if (Ext.dom.Element.isAncestor(target, activeEl)) {
                        start = activeEl.selectionStart;
                        end = activeEl.selectionEnd;

                        if (Ext.isNumber(start) && Ext.isNumber(end)) { 
                            
                            
                            
                            
                            return makeSelectionRestoreFn(activeEl, start, end);
                        }
                    }
                }
            }

            return Ext.emptyFn; 
        },

        getViewWidth: function(full) {
            return full ? Ext.dom.Element.getDocumentWidth() : Ext.dom.Element.getViewportWidth();
        },

        getViewHeight: function(full) {
            return full ? Ext.dom.Element.getDocumentHeight() : Ext.dom.Element.getViewportHeight();
        },

        getDocumentHeight: function() {
            return Math.max(!isCSS1 ? doc.body.scrollHeight : doc.documentElement.scrollHeight, Ext.dom.Element.getViewportHeight());
        },

        getDocumentWidth: function() {
            return Math.max(!isCSS1 ? doc.body.scrollWidth : doc.documentElement.scrollWidth, Ext.dom.Element.getViewportWidth());
        },

        getViewportHeight: function(){
            return Ext.isIE ?
                   (Ext.isStrict ? doc.documentElement.clientHeight : doc.body.clientHeight) :
                   self.innerHeight;
        },

        getViewportWidth: function() {
            return (!Ext.isStrict && !Ext.isOpera) ? doc.body.clientWidth :
                   Ext.isIE ? doc.documentElement.clientWidth : self.innerWidth;
        },

        getY: function(el) {
            return Ext.dom.Element.getXY(el)[1];
        },

        getX: function(el) {
            return Ext.dom.Element.getXY(el)[0];
        },

        getXY: function(el) {
            var p,
                pe,
                b,
                bt,
                bl,
                dbd,
                x = 0,
                y = 0,
                scroll,
                hasAbsolute,
                bd = (doc.body || doc.documentElement),
                ret = [0,0];

            el = Ext.getDom(el);

            if(el != bd){
                hasAbsolute = fly(el).isStyle("position", "absolute");

                if (el.getBoundingClientRect) {
                    b = el.getBoundingClientRect();
                    scroll = fly(document).getScroll();
                    ret = [Math.round(b.left + scroll.left), Math.round(b.top + scroll.top)];
                } else {
                    p = el;

                    while (p) {
                        pe = fly(p);
                        x += p.offsetLeft;
                        y += p.offsetTop;

                        hasAbsolute = hasAbsolute || pe.isStyle("position", "absolute");

                        if (Ext.isGecko) {
                            y += bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
                            x += bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;

                            if (p != el && !pe.isStyle('overflow','visible')) {
                                x += bl;
                                y += bt;
                            }
                        }
                        p = p.offsetParent;
                    }

                    if (Ext.isSafari && hasAbsolute) {
                        x -= bd.offsetLeft;
                        y -= bd.offsetTop;
                    }

                    if (Ext.isGecko && !hasAbsolute) {
                        dbd = fly(bd);
                        x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
                        y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
                    }

                    p = el.parentNode;
                    while (p && p != bd) {
                        if (!Ext.isOpera || (p.tagName != 'TR' && !fly(p).isStyle("display", "inline"))) {
                            x -= p.scrollLeft;
                            y -= p.scrollTop;
                        }
                        p = p.parentNode;
                    }
                    ret = [x,y];
                }
            }
            return ret;
        },

        setXY: function(el, xy) {
            (el = Ext.fly(el, '_setXY')).position();

            var pts = el.translatePoints(xy),
                style = el.dom.style,
                pos;

            for (pos in pts) {
                if (!isNaN(pts[pos])) {
                    style[pos] = pts[pos] + "px";
                }
            }
        },

        setX: function(el, x) {
            Ext.dom.Element.setXY(el, [x, false]);
        },

        setY: function(el, y) {
            Ext.dom.Element.setXY(el, [false, y]);
        },

        
        serializeForm: function(form) {
            var fElements = form.elements || (document.forms[form] || Ext.getDom(form)).elements,
                hasSubmit = false,
                encoder = encodeURIComponent,
                name,
                data = '',
                type,
                hasValue;

            Ext.each(fElements, function(element){
                name = element.name;
                type = element.type;

                if (!element.disabled && name) {
                    if (/select-(one|multiple)/i.test(type)) {
                        Ext.each(element.options, function(opt){
                            if (opt.selected) {
                                hasValue = opt.hasAttribute ? opt.hasAttribute('value') : opt.getAttributeNode('value').specified;
                                data += Ext.String.format("{0}={1}&", encoder(name), encoder(hasValue ? opt.value : opt.text));
                            }
                        });
                    } else if (!(/file|undefined|reset|button/i.test(type))) {
                        if (!(/radio|checkbox/i.test(type) && !element.checked) && !(type == 'submit' && hasSubmit)) {
                            data += encoder(name) + '=' + encoder(element.value) + '&';
                            hasSubmit = /submit/i.test(type);
                        }
                    }
                }
            });
            return data.substr(0, data.length - 1);
        }
    });
})();


Ext.dom.AbstractElement.override({

    
    getAnchorXY: function(anchor, local, size) {
        
        
        anchor = (anchor || "tl").toLowerCase();
        size = size || {};

        var me = this,
            vp = me.dom == document.body || me.dom == document,
            width = size.width || vp ? window.innerWidth: me.getWidth(),
            height = size.height || vp ? window.innerHeight: me.getHeight(),
            xy,
            rnd = Math.round,
            myXY = me.getXY(),
            extraX = vp ? 0: !local ? myXY[0] : 0,
            extraY = vp ? 0: !local ? myXY[1] : 0,
            hash = {
                c: [rnd(width * 0.5), rnd(height * 0.5)],
                t: [rnd(width * 0.5), 0],
                l: [0, rnd(height * 0.5)],
                r: [width, rnd(height * 0.5)],
                b: [rnd(width * 0.5), height],
                tl: [0, 0],
                bl: [0, height],
                br: [width, height],
                tr: [width, 0]
            };

        xy = hash[anchor];
        return [xy[0] + extraX, xy[1] + extraY];
    },

    alignToRe: /^([a-z]+)-([a-z]+)(\?)?$/,

    
    getAlignToXY: function(el, position, offsets, local) {
        local = !!local;
        el = Ext.get(el);

        if (!el || !el.dom) {
            throw new Error("Element.alignToXY with an element that doesn't exist");
        }
        offsets = offsets || [0, 0];

        if (!position || position == '?') {
            position = 'tl-bl?';
        }
        else if (! (/-/).test(position) && position !== "") {
            position = 'tl-' + position;
        }
        position = position.toLowerCase();

        var me = this,
            matches = position.match(this.alignToRe),
            dw = window.innerWidth,
            dh = window.innerHeight,
            p1 = "",
            p2 = "",
            a1,
            a2,
            x,
            y,
            swapX,
            swapY,
            p1x,
            p1y,
            p2x,
            p2y,
            width,
            height,
            region,
            constrain;

        if (!matches) {
            throw "Element.alignTo with an invalid alignment " + position;
        }

        p1 = matches[1];
        p2 = matches[2];
        constrain = !!matches[3];

        
        
        a1 = me.getAnchorXY(p1, true);
        a2 = el.getAnchorXY(p2, local);

        x = a2[0] - a1[0] + offsets[0];
        y = a2[1] - a1[1] + offsets[1];

        if (constrain) {
            width = me.getWidth();
            height = me.getHeight();

            region = el.getPageBox();

            
            
            
            p1y = p1.charAt(0);
            p1x = p1.charAt(p1.length - 1);
            p2y = p2.charAt(0);
            p2x = p2.charAt(p2.length - 1);

            swapY = ((p1y == "t" && p2y == "b") || (p1y == "b" && p2y == "t"));
            swapX = ((p1x == "r" && p2x == "l") || (p1x == "l" && p2x == "r"));

            if (x + width > dw) {
                x = swapX ? region.left - width: dw - width;
            }
            if (x < 0) {
                x = swapX ? region.right: 0;
            }
            if (y + height > dh) {
                y = swapY ? region.top - height: dh - height;
            }
            if (y < 0) {
                y = swapY ? region.bottom: 0;
            }
        }

        return [x, y];
    },

    
    getAnchor: function(){
        var dom = this.dom;
            if (!dom) {
                return;
            }
            var anchor = this.self.data.call(this.self, dom, '_anchor');

        if(!anchor){
            anchor = this.self.data.call(this.self, dom, '_anchor', {});
        }
        return anchor;
    },

    
    adjustForConstraints: function(xy, parent) {
        var vector = this.getConstrainVector(parent, xy);
        if (vector) {
            xy[0] += vector[0];
            xy[1] += vector[1];
        }
        return xy;
    }

});


Ext.dom.AbstractElement.addMethods({
    
    appendChild: function(el) {
        return Ext.get(el).appendTo(this);
    },

    
    appendTo: function(el) {
        Ext.getDom(el).appendChild(this.dom);
        return this;
    },

    
    insertBefore: function(el) {
        el = Ext.getDom(el);
        el.parentNode.insertBefore(this.dom, el);
        return this;
    },

    
    insertAfter: function(el) {
        el = Ext.getDom(el);
        el.parentNode.insertBefore(this.dom, el.nextSibling);
        return this;
    },

    
    insertFirst: function(el, returnDom) {
        el = el || {};
        if (el.nodeType || el.dom || typeof el == 'string') { 
            el = Ext.getDom(el);
            this.dom.insertBefore(el, this.dom.firstChild);
            return !returnDom ? Ext.get(el) : el;
        }
        else { 
            return this.createChild(el, this.dom.firstChild, returnDom);
        }
    },

    
    insertSibling: function(el, where, returnDom){
        var me = this, rt,
        isAfter = (where || 'before').toLowerCase() == 'after',
        insertEl;

        if(Ext.isArray(el)){
            insertEl = me;
            Ext.each(el, function(e) {
                rt = Ext.fly(insertEl, '_internal').insertSibling(e, where, returnDom);
                if(isAfter){
                    insertEl = rt;
                }
            });
            return rt;
        }

        el = el || {};

        if(el.nodeType || el.dom){
            rt = me.dom.parentNode.insertBefore(Ext.getDom(el), isAfter ? me.dom.nextSibling : me.dom);
            if (!returnDom) {
                rt = Ext.get(rt);
            }
        }else{
            if (isAfter && !me.dom.nextSibling) {
                rt = Ext.core.DomHelper.append(me.dom.parentNode, el, !returnDom);
            } else {
                rt = Ext.core.DomHelper[isAfter ? 'insertAfter' : 'insertBefore'](me.dom, el, !returnDom);
            }
        }
        return rt;
    },

    
    replace: function(el) {
        el = Ext.get(el);
        this.insertBefore(el);
        el.remove();
        return this;
    },

    
    replaceWith: function(el){
        var me = this;

        if(el.nodeType || el.dom || typeof el == 'string'){
            el = Ext.get(el);
            me.dom.parentNode.insertBefore(el, me.dom);
        }else{
            el = Ext.core.DomHelper.insertBefore(me.dom, el);
        }

        delete Ext.cache[me.id];
        Ext.removeNode(me.dom);
        me.id = Ext.id(me.dom = el);
        Ext.dom.AbstractElement.addToCache(me.isFlyweight ? new Ext.dom.AbstractElement(me.dom) : me);
        return me;
    },

    
    createChild: function(config, insertBefore, returnDom) {
        config = config || {tag:'div'};
        if (insertBefore) {
            return Ext.core.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
        }
        else {
            return Ext.core.DomHelper[!this.dom.firstChild ? 'insertFirst' : 'append'](this.dom, config,  returnDom !== true);
        }
    },

    
    wrap: function(config, returnDom) {
        var newEl = Ext.core.DomHelper.insertBefore(this.dom, config || {tag: "div"}, !returnDom),
            d = newEl.dom || newEl;

        d.appendChild(this.dom);
        return newEl;
    },

    
    insertHtml: function(where, html, returnEl) {
        var el = Ext.core.DomHelper.insertHtml(where, this.dom, html);
        return returnEl ? Ext.get(el) : el;
    }
});


(function(){

var Element = Ext.dom.AbstractElement;

Element.override({

    
    getX: function(el) {
        return this.getXY(el)[0];
    },

    
    getY: function(el) {
        return this.getXY(el)[1];
    },

    
    getXY: function() {
        
        var point = window.webkitConvertPointFromNodeToPage(this.dom, new WebKitPoint(0, 0));
        return [point.x, point.y];
    },

    
    getOffsetsTo: function(el){
        var o = this.getXY(),
            e = Ext.fly(el, '_internal').getXY();
        return [o[0]-e[0],o[1]-e[1]];
    },

    
    setX: function(x){
        return this.setXY([x, this.getY()]);
    },

    
    setY: function(y) {
        return this.setXY([this.getX(), y]);
    },

    
    setLeft: function(left) {
        this.setStyle('left', Element.addUnits(left));
        return this;
    },

    
    setTop: function(top) {
        this.setStyle('top', Element.addUnits(top));
        return this;
    },

    
    setRight: function(right) {
        this.setStyle('right', Element.addUnits(right));
        return this;
    },

    
    setBottom: function(bottom) {
        this.setStyle('bottom', Element.addUnits(bottom));
        return this;
    },

    
    setXY: function(pos) {
        var me = this;

        if (arguments.length > 1) {
            pos = [pos, arguments[1]];
        }

        
        var pts = me.translatePoints(pos),
                style = me.dom.style;

        for (pos in pts) {
            if (!pts.hasOwnProperty(pos)) {
                continue;
            }
            if (!isNaN(pts[pos])) style[pos] = pts[pos] + "px";
        }
        return me;
    },

    
    getLeft: function(local) {
        return parseInt(this.getStyle('left'), 10) || 0;
    },

    
    getRight: function(local) {
        return parseInt(this.getStyle('right'), 10) || 0;
    },

    
    getTop: function(local) {
        return parseInt(this.getStyle('top'), 10) || 0;
    },

    
    getBottom: function(local) {
        return parseInt(this.getStyle('bottom'), 10) || 0;
    },

    
    translatePoints: function(x, y) {
        y = isNaN(x[1]) ? y : x[1];
        x = isNaN(x[0]) ? x : x[0];
        var me = this,
            relative = me.isStyle('position', 'relative'),
            o = me.getXY(),
            l = parseInt(me.getStyle('left'), 10),
            t = parseInt(me.getStyle('top'), 10);

        l = !isNaN(l) ? l : (relative ? 0 : me.dom.offsetLeft);
        t = !isNaN(t) ? t : (relative ? 0 : me.dom.offsetTop);

        return {left: (x - o[0] + l), top: (y - o[1] + t)};
    },

    
    setBox: function(box) {
        var me = this,
            width = box.width,
            height = box.height,
            top = box.top,
            left = box.left;

        if (left !== undefined) {
            me.setLeft(left);
        }
        if (top !== undefined) {
            me.setTop(top);
        }
        if (width !== undefined) {
            me.setWidth(width);
        }
        if (height !== undefined) {
            me.setHeight(height);
        }

        return this;
    },

    
    getBox: function(contentBox, local) {
        var me = this,
            dom = me.dom,
            width = dom.offsetWidth,
            height = dom.offsetHeight,
            xy, box, l, r, t, b;

        if (!local) {
            xy = me.getXY();
        }
        else if (contentBox) {
            xy = [0,0];
        }
        else {
            xy = [parseInt(me.getStyle("left"), 10) || 0, parseInt(me.getStyle("top"), 10) || 0];
        }

        if (!contentBox) {
            box = {
                x: xy[0],
                y: xy[1],
                0: xy[0],
                1: xy[1],
                width: width,
                height: height
            };
        }
        else {
            l = me.getBorderWidth.call(me, "l") + me.getPadding.call(me, "l");
            r = me.getBorderWidth.call(me, "r") + me.getPadding.call(me, "r");
            t = me.getBorderWidth.call(me, "t") + me.getPadding.call(me, "t");
            b = me.getBorderWidth.call(me, "b") + me.getPadding.call(me, "b");
            box = {
                x: xy[0] + l,
                y: xy[1] + t,
                0: xy[0] + l,
                1: xy[1] + t,
                width: width - (l + r),
                height: height - (t + b)
            };
        }

        box.left = box.x;
        box.top = box.y;
        box.right = box.x + box.width;
        box.bottom = box.y + box.height;

        return box;
    },

    
    getPageBox: function(getRegion) {
        var me = this,
            el = me.dom,
            w = el.offsetWidth,
            h = el.offsetHeight,
            xy = me.getXY(),
            t = xy[1],
            r = xy[0] + w,
            b = xy[1] + h,
            l = xy[0];

        if (!el) {
            return new Ext.util.Region();
        }

        if (getRegion) {
            return new Ext.util.Region(t, r, b, l);
        }
        else {
            return {
                left: l,
                top: t,
                width: w,
                height: h,
                right: r,
                bottom: b
            };
        }
    }
});

})();


(function(){
    
    var Element = Ext.dom.AbstractElement,
        view = document.defaultView,
        trimRe = /^\s+|\s+$/g,
        wordsRe = /\w/g,
        spacesRe = /\s+/,
        transparentRe = /^(?:transparent|(?:rgba[(](?:\s*\d+\s*[,]){3}\s*0\s*[)]))$/i,
        hasClassList = Ext.supports.ClassList,

        PADDING = 'padding',
        MARGIN = 'margin',
        BORDER = 'border',
        LEFT_SUFFIX = '-left',
        RIGHT_SUFFIX = '-right',
        TOP_SUFFIX = '-top',
        BOTTOM_SUFFIX = '-bottom',
        WIDTH = '-width',

        supportsTransparentColor = Ext.supports.TransparentColor,

        
        borders = {l: BORDER + LEFT_SUFFIX + WIDTH, r: BORDER + RIGHT_SUFFIX + WIDTH, t: BORDER + TOP_SUFFIX + WIDTH, b: BORDER + BOTTOM_SUFFIX + WIDTH},
        paddings = {l: PADDING + LEFT_SUFFIX, r: PADDING + RIGHT_SUFFIX, t: PADDING + TOP_SUFFIX, b: PADDING + BOTTOM_SUFFIX},
        margins = {l: MARGIN + LEFT_SUFFIX, r: MARGIN + RIGHT_SUFFIX, t: MARGIN + TOP_SUFFIX, b: MARGIN + BOTTOM_SUFFIX};


    Element.override({

        
        styleHooks: {},

        
        addStyles: function(sides, styles){
            var totalSize = 0,
                sidesArr = sides.match(wordsRe),
                i = 0,
                len = sidesArr.length,
                side, size;
            for (; i < len; i++) {
                side = sidesArr[i];
                size = side && parseInt(this.getStyle(styles[side]), 10);
                if (size) {
                    totalSize += Math.abs(size);
                }
            }
            return totalSize;
        },

        
        addCls: hasClassList ?
            function (className) {
                if (String(className).indexOf('undefined') > -1) {
                    Ext.Logger.warn("called with an undefined className: " + className);
                }
                var me = this,
                    dom = me.dom,
                    classList,
                    newCls,
                    i,
                    len,
                    cls;

                if (typeof(className) == 'string') {
                    
                    className = className.replace(trimRe, '').split(spacesRe);
                }

                
                
                if (dom && className && !!(len = className.length)) {
                    if (!dom.className) {
                        dom.className = className.join(' ');
                    } else {
                        classList = dom.classList;
                        for (i = 0; i < len; ++i) {
                            cls = className[i];
                            if (!classList.contains(cls)) {
                                if (newCls) {
                                    newCls.push(cls);
                                } else {
                                    newCls = dom.className.replace(trimRe, '');
                                    newCls = newCls ? [newCls, cls] : [cls];
                                }
                            }
                        }

                        if (newCls) {
                            dom.className = newCls.join(' '); 
                        }
                    }
                }
                return me;
            } :
            function(className) {
                if (String(className).indexOf('undefined') > -1) {
                    Ext.Logger.warn("called with an undefined className: '" + className + "'");
                }
                var me = this,
                    dom = me.dom,
                    changed,
                    elClasses,
                    currentClsMap,
                    i,
                    len,
                    cls;

                if (typeof(className) == 'string') {
                    
                    className = className.replace(trimRe, '').split(spacesRe);
                }

                
                
                
                
                
                
                
                
                
                
                
                if (dom && className && className.length) {
                    elClasses = dom.className.replace(trimRe, '');
                    elClasses = elClasses ? elClasses.split(spacesRe) : null;

                    if (elClasses && !!(len = elClasses.length)) {
                        currentClsMap = {};
                        for (i = 0; i < len; ++i) {
                            currentClsMap[elClasses[i]] = 1;
                        }
                        

                        for (i = 0, len = className.length; i < len; ++i) {
                            cls = className[i];
                            if (!currentClsMap[cls]) {
                                elClasses.push(cls);
                                changed = true;
                            }
                        }
                    } else {
                        elClasses = className; 
                        changed = true;
                    }

                    if (changed) {
                        dom.className = elClasses.join(' '); 
                    }
                }

                return me;
            },


        
        removeCls: function(className) {
            var me = this,
                dom = me.dom,
                i,
                len,
                cls,
                elClasses,
                newCls,
                removeMap;

            if (typeof(className) == 'string') {
                
                className = className.replace(trimRe, '').split(spacesRe);
            }

            
            
            
            
            
            
            
            
            if (dom && dom.className && className && !!(len = className.length)) {
                if (len == 1 && hasClassList) {
                    dom.classList.remove(className[0]); 
                } else {
                    removeMap = {}; 
                    for (i = 0, len = className.length; i < len; ++i) {
                        removeMap[className[i]] = 1;
                    }

                    elClasses = dom.className.replace(trimRe, '').split(spacesRe);
                    newCls = [];
                    for (i = 0, len = elClasses.length; i < len; i++) {
                        cls = elClasses[i];
                        if (!removeMap[cls]) {
                            newCls.push(cls); 
                        }
                    }

                    dom.className = newCls.join(' '); 
                }
            }

            return me;
        },

        
        radioCls: function(className) {
            var cn = this.dom.parentNode.childNodes,
                v;
            className = Ext.isArray(className) ? className: [className];
            for (var i = 0, len = cn.length; i < len; i++) {
                v = cn[i];
                if (v && v.nodeType == 1) {
                    Ext.fly(v, '_internal').removeCls(className);
                }
            };
            return this.addCls(className);
        },

        
        toggleCls: hasClassList ?
            function (className) {
                var me = this,
                    dom = me.dom;

                if (dom) {
                    dom.classList.toggle(className.replace(trimRe, ''));
                }

                return me;
            } :
            function(className) {
                var me = this;
                return me.hasCls(className) ? me.removeCls(className) : me.addCls(className);
            },

        
        hasCls: hasClassList ?
            function (className) {
                var dom = this.dom;
                return dom ? dom.classList.contains(className) : false;
            } :
            function(className) {
                var dom = this.dom;
                return dom ? className && (' '+dom.className+' ').indexOf(' '+className+' ') != -1 : false;
            },

        
        replaceCls: function(oldClassName, newClassName){
            return this.removeCls(oldClassName).addCls(newClassName);
        },

        
        isStyle: function(style, val) {
            return this.getStyle(style) == val;
        },

        
        getStyle: function(prop) {
            var me = this,
                dom = me.dom,
                hook = me.styleHooks[prop],
                cs, result;

            if (dom == document) {
                return null;
            }
            if (!hook) {
                me.styleHooks[prop] = hook = { name: Element.normalize(prop) };
            }
            if (hook.get) {
                return hook.get(dom, me);
            }

            cs = view.getComputedStyle(dom, '');

            
            
            result = (cs && cs[hook.name]); 

            
            if (!supportsTransparentColor && result == 'rgba(0, 0, 0, 0)') {
                result = 'transparent';
            }
            
            

            return result;
        },

        
        isTransparent: function (prop) {
            var value = this.getStyle(prop);
            return value ? transparentRe.test(value) : false;
        },

        
        setStyle: function(prop, value) {
            var me = this,
                dom = me.dom,
                hooks = me.styleHooks,
                style = dom.style,
                valueFrom = Ext.valueFrom,
                name = prop,
                hook;

            
            if (typeof name == 'string') {
                hook = hooks[name];
                if (!hook) {
                    hooks[name] = hook = { name: Element.normalize(name) };
                }
                value = valueFrom(value, '');
                if (hook.set) {
                    hook.set(dom, value, me);
                } else {
                    style[hook.name] = value;
                }
            } else {
                for (name in prop) {
                    if (prop.hasOwnProperty(name)) {
                        hook = hooks[name];
                        if (!hook) {
                            hooks[name] = hook = { name: Element.normalize(name) };
                        }
                        value = valueFrom(prop[name], '');
                        if (hook.set) {
                            hook.set(dom, value, me);
                        } else {
                            style[hook.name] = value;
                        }
                    }
                }
            }

            return me;
        },

        
        getHeight: function(contentHeight) {
            var dom = this.dom,
                height = contentHeight ? (dom.clientHeight - this.getPadding("tb")) : dom.offsetHeight;
            return height > 0 ? height: 0;
        },

        
        getWidth: function(contentWidth) {
            var dom = this.dom,
                width = contentWidth ? (dom.clientWidth - this.getPadding("lr")) : dom.offsetWidth;
            return width > 0 ? width: 0;
        },

        
        setWidth: function(width) {
            var me = this;
                me.dom.style.width = Element.addUnits(width);
            return me;
        },

        
        setHeight: function(height) {
            var me = this;
                me.dom.style.height = Element.addUnits(height);
            return me;
        },

        
        getBorderWidth: function(side){
            return this.addStyles(side, borders);
        },

        
        getPadding: function(side){
            return this.addStyles(side, paddings);
        },

        margins : margins,

        
        applyStyles: function(styles) {
            if (styles) {
                var i,
                    len,
                    dom = this.dom;

                if (typeof styles == 'function') {
                    styles = styles.call();
                }
                if (typeof styles == 'string') {
                    styles = Ext.util.Format.trim(styles).split(/\s*(?::|;)\s*/);
                    for (i = 0, len = styles.length; i < len;) {
                        dom.style[Element.normalize(styles[i++])] = styles[i++];
                    }
                }
                else if (typeof styles == 'object') {
                    this.setStyle(styles);
                }
            }
        },

        
        setSize: function(width, height) {
            var me = this,
                style = me.dom.style;

            if (Ext.isObject(width)) {
                
                height = width.height;
                width = width.width;
            }

            style.width = Element.addUnits(width);
            style.height = Element.addUnits(height);
            return me;
        },

        
        getViewSize: function() {
            var doc = document,
                dom = this.dom;

            if (dom == doc || dom == doc.body) {
                return {
                    width: Element.getViewportWidth(),
                    height: Element.getViewportHeight()
                };
            }
            else {
                return {
                    width: dom.clientWidth,
                    height: dom.clientHeight
                };
            }
        },

        
        getSize: function(contentSize) {
            var dom = this.dom;
            return {
                width: Math.max(0, contentSize ? (dom.clientWidth - this.getPadding("lr")) : dom.offsetWidth),
                height: Math.max(0, contentSize ? (dom.clientHeight - this.getPadding("tb")) : dom.offsetHeight)
            };
        },

        
        repaint: function(){
            var dom = this.dom;
            this.addCls(Ext.baseCSSPrefix + 'repaint');
            setTimeout(function(){
                Ext.fly(dom).removeCls(Ext.baseCSSPrefix + 'repaint');
            }, 1);
            return this;
        },

        
        getMargin: function(side){
            var me = this,
                hash = {t:"top", l:"left", r:"right", b: "bottom"},
                o = {},
                key;

            if (!side) {
                for (key in me.margins){
                    o[hash[key]] = parseFloat(me.getStyle(me.margins[key])) || 0;
                }
                return o;
            } else {
                return me.addStyles.call(me, side, me.margins);
            }
        },

        
        mask: function(msg, msgCls, transparent) {
            var me = this,
                dom = me.dom,
                el = Ext.Element.data(dom, 'mask'),
                mask,
                size,
                cls = '';

            me.addCls('x-masked');
            if (me.getStyle("position") == "static") {
                me.addCls('x-masked-relative');
            }
            if (el) {
                el.remove();
            }
            if (Ext.isString(msgCls) && !Ext.isEmpty(msgCls)) {
                cls = ' ' + msgCls;
            }
            else {
                if (msgCls) {
                    cls = ' x-mask-gray';
                }
            }

            mask = me.createChild({
                cls: 'x-mask' + ((transparent !== false) ? '' : ' x-mask-gray'),
                html: msg ? ('<div class="' + (msgCls || 'x-mask-message') + '">' + msg + '</div>') : ''
            });

            size = me.getSize();

            Ext.Element.data(dom, 'mask', mask);

            if (dom === document.body) {
                size.height = window.innerHeight;
                if (me.orientationHandler) {
                    Ext.EventManager.unOrientationChange(me.orientationHandler, me);
                }

                me.orientationHandler = function() {
                    size = me.getSize();
                    size.height = window.innerHeight;
                    mask.setSize(size);
                };

                Ext.EventManager.onOrientationChange(me.orientationHandler, me);
            }
            mask.setSize(size);
            if (Ext.is.iPad) {
                Ext.repaint();
            }
        },

        
        unmask: function() {
            var me = this,
                dom = me.dom,
                mask = Ext.Element.data(dom, 'mask');

            if (mask) {
                mask.remove();
                Ext.Element.data(dom, 'mask', undefined);
            }
            me.removeCls(['x-masked', 'x-masked-relative']);

            if (dom === document.body) {
                Ext.EventManager.unOrientationChange(me.orientationHandler, me);
                delete me.orientationHandler;
            }
        }
    });

    
    Element.populateStyleMap = function (map, order) {
        var baseStyles = ['margin-', 'padding-', 'border-width-'],
            beforeAfter = ['before', 'after'],
            index, style, name, i;

        for (index = baseStyles.length; index--; ) {
            for (i = 2; i--; ) {
                style = baseStyles[index] + beforeAfter[i]; 
                
                map[Element.normalize(style)] = map[style] = {
                    name: Element.normalize(baseStyles[index] + order[i])
                };
            }
        }
    };
})();

Ext.onReady(function () {
    var view = document.defaultView,
        Element = Ext.dom.AbstractElement,
        supports = Ext.supports;

    function fixRightMargin (dom) {
        var cs = view.getComputedStyle(dom, ''),
            result = cs ? cs.marginRight : null,
            style, display;

        
        
        if (result != '0px') {
            style = dom.style;
            display = style.display;
            style.display = 'inline-block';
            result = view.getComputedStyle(dom, null).marginRight;
            style.display = display;
        }

        return result;
    }

    function fixRightMarginAndInputFocus (dom) {
        var cs = view.getComputedStyle(dom, ''),
            result = cs ? cs.marginRight : null,
            style, cleaner, display;

        if (result != '0px') {
            style = dom.style;
            cleaner = Element.getRightMarginFixCleaner(dom);
            display = style.display;
            style.display = 'inline-block';
            result = view.getComputedStyle(dom, '').marginRight;
            style.display = display;
            cleaner();
        }

        return result;
    }

    var styleHooks = Element.prototype.styleHooks;

    
    Element.populateStyleMap(styleHooks, ['left', 'right']);

    
    
    if (supports.init) {
        supports.init();
    }

    
    if (!supports.RightMargin) {
        styleHooks['margin-right'] = styleHooks.marginRight = {
            name: 'marginRight',
            
            
            get: (supports.DisplayChangeInputSelectionBug || supports.DisplayChangeTextAreaSelectionBug) ?
                    fixRightMarginAndInputFocus : fixRightMargin
        };
    }
});


Ext.dom.AbstractElement.override({
    
    findParent: function(simpleSelector, maxDepth, returnEl) {
        var p = this.dom,
            b = document.body,
            depth = 0,
            stopEl;

        maxDepth = maxDepth || 50;
        if (isNaN(maxDepth)) {
            stopEl = Ext.getDom(maxDepth);
            maxDepth = Number.MAX_VALUE;
        }
        while (p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl) {
            if (Ext.DomQuery.is(p, simpleSelector)) {
                return returnEl ? Ext.get(p) : p;
            }
            depth++;
            p = p.parentNode;
        }
        return null;
    },

    
    findParentNode: function(simpleSelector, maxDepth, returnEl) {
        var p = Ext.fly(this.dom.parentNode, '_internal');
        return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
    },

    
    up: function(simpleSelector, maxDepth) {
        return this.findParentNode(simpleSelector, maxDepth, true);
    },

    
    select: function(selector, composite) {
        return Ext.dom.Element.select(selector, this.dom, composite);
    },

    
    query: function(selector) {
        return Ext.DomQuery.select(selector, this.dom);
    },

    
    down: function(selector, returnDom) {
        var n = Ext.DomQuery.selectNode(selector, this.dom);
        return returnDom ? n : Ext.get(n);
    },

    
    child: function(selector, returnDom) {
        var node,
            me = this,
            id;
        id = Ext.get(me).id;
        
        id = id.replace(/[\.:]/g, "\\$0");
        node = Ext.DomQuery.selectNode('#' + id + " > " + selector, me.dom);
        return returnDom ? node : Ext.get(node);
    },

     
    parent: function(selector, returnDom) {
        return this.matchNode('parentNode', 'parentNode', selector, returnDom);
    },

     
    next: function(selector, returnDom) {
        return this.matchNode('nextSibling', 'nextSibling', selector, returnDom);
    },

    
    prev: function(selector, returnDom) {
        return this.matchNode('previousSibling', 'previousSibling', selector, returnDom);
    },


    
    first: function(selector, returnDom) {
        return this.matchNode('nextSibling', 'firstChild', selector, returnDom);
    },

    
    last: function(selector, returnDom) {
        return this.matchNode('previousSibling', 'lastChild', selector, returnDom);
    },

    matchNode: function(dir, start, selector, returnDom) {
        if (!this.dom) {
            return null;
        }

        var n = this.dom[start];
        while (n) {
            if (n.nodeType == 1 && (!selector || Ext.DomQuery.is(n, selector))) {
                return !returnDom ? Ext.get(n) : n;
            }
            n = n[dir];
        }
        return null;
    },

    isAncestor: function(element) {
        return this.self.isAncestor.call(this.self, this.dom, element);
    }
});





Ext.define('Ext.AbstractPlugin', {
    disabled: false,

    constructor: function(config) {
        if (!config.cmp && Ext.global.console) {
            Ext.global.console.warn("Attempted to attach a plugin ");
        }
        Ext.apply(this, config);
    },

    getCmp: function() {
        return this.cmp;
    },

    
    init: Ext.emptyFn,

    
    destroy: Ext.emptyFn,

    
    enable: function() {
        this.disabled = false;
    },

    
    disable: function() {
        this.disabled = true;
    }
});

Ext.define('Ext.ComponentManager', {
    alternateClassName: 'Ext.ComponentMgr',
    singleton: true,

    constructor: function() {
        var map = {};

        
        this.all = {
            map: map,

            getArray: function() {
                var list = [],
                    id;

                for (id in map) {
                    list.push(map[id]);
                }

                return list;
            }
        };

        this.map = map;
    },

    
    register: function(component) {
        this.map[component.getId()] = component;
    },

    
    unregister: function(component) {
        delete this.map[component.getId()];
    },

    
    isRegistered : function(component){
        return this.map[component] !== undefined;
    },

    
    get: function(id) {
        return this.map[id];
    },

    
    create: function(component, defaultType) {
        if (component.isComponent) {
            return component;
        }
        else if (Ext.isString(component)) {
            return Ext.createByAlias('widget.' + component);
        }
        else {
            var type = component.xtype || defaultType;

            return Ext.createByAlias('widget.' + type, component);
        }
    },

    registerType: Ext.emptyFn
});


Ext.define('Ext.ComponentQuery', {
    singleton: true,
    uses: ['Ext.ComponentManager']
}, function() {

    var cq = this,

        
        
        filterFnPattern = [
            'var r = [],',
                'i = 0,',
                'it = items,',
                'l = it.length,',
                'c;',
            'for (; i < l; i++) {',
                'c = it[i];',
                'if (c.{0}) {',
                   'r.push(c);',
                '}',
            '}',
            'return r;'
        ].join(''),

        filterItems = function(items, operation) {
            
            
            
            return operation.method.apply(this, [ items ].concat(operation.args));
        },

        getItems = function(items, mode) {
            var result = [],
                i = 0,
                length = items.length,
                candidate,
                deep = mode !== '>';
                
            for (; i < length; i++) {
                candidate = items[i];
                if (candidate.getRefItems) {
                    result = result.concat(candidate.getRefItems(deep));
                }
            }
            return result;
        },

        getAncestors = function(items) {
            var result = [],
                i = 0,
                length = items.length,
                candidate;
            for (; i < length; i++) {
                candidate = items[i];
                while (!!(candidate = (candidate.ownerCt || candidate.floatParent))) {
                    result.push(candidate);
                }
            }
            return result;
        },

        
        filterByXType = function(items, xtype, shallow) {
            if (xtype === '*') {
                return items.slice();
            }
            else {
                var result = [],
                    i = 0,
                    length = items.length,
                    candidate;
                for (; i < length; i++) {
                    candidate = items[i];
                    if (candidate.isXType(xtype, shallow)) {
                        result.push(candidate);
                    }
                }
                return result;
            }
        },

        
        filterByClassName = function(items, className) {
            var EA = Ext.Array,
                result = [],
                i = 0,
                length = items.length,
                candidate;
            for (; i < length; i++) {
                candidate = items[i];
                if (candidate.el ? candidate.el.hasCls(className) : EA.contains(candidate.initCls(), className)) {
                    result.push(candidate);
                }
            }
            return result;
        },

        
        filterByAttribute = function(items, property, operator, value) {
            var result = [],
                i = 0,
                length = items.length,
                candidate;
            for (; i < length; i++) {
                candidate = items[i];
                if (!value ? !!candidate[property] : (String(candidate[property]) === value)) {
                    result.push(candidate);
                }
                else if (candidate.config) {
                    if (!value ? !!candidate.config[property] : (String(candidate.config[property]) === value)) {
                        result.push(candidate);
                    }
                }
            }
            return result;
        },

        
        filterById = function(items, id) {
            var result = [],
                i = 0,
                length = items.length,
                candidate;
            for (; i < length; i++) {
                candidate = items[i];
                if (candidate.getItemId() === id) {
                    result.push(candidate);
                }
            }
            return result;
        },

        
        filterByPseudo = function(items, name, value) {
            return cq.pseudos[name](items, value);
        },

        
        
        modeRe = /^(\s?([>\^])\s?|\s|$)/,

        
        tokenRe = /^(#)?([\w\-]+|\*)(?:\((true|false)\))?/,

        matchers = [{
            
            re: /^\.([\w\-]+)(?:\((true|false)\))?/,
            method: filterByXType
        },{
            
            re: /^(?:[\[](?:@)?([\w\-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]])/,
            method: filterByAttribute
        }, {
            
            re: /^#([\w\-]+)/,
            method: filterById
        }, {
            
            re: /^\:([\w\-]+)(?:\(((?:\{[^\}]+\})|(?:(?!\{)[^\s>\/]*?(?!\})))\))?/,
            method: filterByPseudo
        }, {
            
            re: /^(?:\{([^\}]+)\})/,
            method: filterFnPattern
        }];

    cq.Query = Ext.extend(Object, {
        constructor: function(cfg) {
            cfg = cfg || {};
            Ext.apply(this, cfg);
        },

        
        execute : function(root) {
            var operations = this.operations,
                i = 0,
                length = operations.length,
                operation,
                workingItems;

            
            if (!root) {
                workingItems = Ext.ComponentManager.all.getArray();
            }
            
            else if (Ext.isArray(root)) {
                workingItems = root;
            }

            
            
            for (; i < length; i++) {
                operation = operations[i];

                
                
                
                
                
                
                if (operation.mode === '^') {
                    workingItems = getAncestors(workingItems || [root]);
                }
                else if (operation.mode) {
                    workingItems = getItems(workingItems || [root], operation.mode);
                }
                else {
                    workingItems = filterItems(workingItems || getItems([root]), operation);
                }

                
                
                if (i === length -1) {
                    return workingItems;
                }
            }
            return [];
        },

        is: function(component) {
            var operations = this.operations,
                components = Ext.isArray(component) ? component : [component],
                originalLength = components.length,
                lastOperation = operations[operations.length-1],
                ln, i;

            components = filterItems(components, lastOperation);
            if (components.length === originalLength) {
                if (operations.length > 1) {
                    for (i = 0, ln = components.length; i < ln; i++) {
                        if (Ext.Array.indexOf(this.execute(), components[i]) === -1) {
                            return false;
                        }
                    }
                }
                return true;
            }
            return false;
        }
    });

    Ext.apply(this, {

        
        cache: {},

        
        pseudos: {
            not: function(components, selector){
                var CQ = Ext.ComponentQuery,
                    i = 0,
                    length = components.length,
                    results = [],
                    index = -1,
                    component;
                
                for(; i < length; ++i) {
                    component = components[i];
                    if (!CQ.is(component, selector)) {
                        results[++index] = component;
                    }
                }
                return results;
            }
        },

        
        query: function(selector, root) {
            var selectors = selector.split(','),
                length = selectors.length,
                i = 0,
                results = [],
                noDupResults = [], 
                dupMatcher = {}, 
                query, resultsLn, cmp;

            for (; i < length; i++) {
                selector = Ext.String.trim(selectors[i]);
                query = this.cache[selector];
                if (!query) {
                    this.cache[selector] = query = this.parse(selector);
                }
                results = results.concat(query.execute(root));
            }

            
            
            if (length > 1) {
                resultsLn = results.length;
                for (i = 0; i < resultsLn; i++) {
                    cmp = results[i];
                    if (!dupMatcher[cmp.id]) {
                        noDupResults.push(cmp);
                        dupMatcher[cmp.id] = true;
                    }
                }
                results = noDupResults;
            }
            return results;
        },

        
        is: function(component, selector) {
            if (!selector) {
                return true;
            }
            var query = this.cache[selector];
            if (!query) {
                this.cache[selector] = query = this.parse(selector);
            }
            return query.is(component);
        },

        parse: function(selector) {
            var operations = [],
                length = matchers.length,
                lastSelector,
                tokenMatch,
                matchedChar,
                modeMatch,
                selectorMatch,
                i, matcher, method;

            
            
            
            while (selector && lastSelector !== selector) {
                lastSelector = selector;

                
                tokenMatch = selector.match(tokenRe);

                if (tokenMatch) {
                    matchedChar = tokenMatch[1];

                    
                    if (matchedChar === '#') {
                        operations.push({
                            method: filterById,
                            args: [Ext.String.trim(tokenMatch[2])]
                        });
                    }
                    
                    
                    else if (matchedChar === '.') {
                        operations.push({
                            method: filterByClassName,
                            args: [Ext.String.trim(tokenMatch[2])]
                        });
                    }
                    
                    
                    else {
                        operations.push({
                            method: filterByXType,
                            args: [Ext.String.trim(tokenMatch[2]), Boolean(tokenMatch[3])]
                        });
                    }

                    
                    selector = selector.replace(tokenMatch[0], '');
                }

                
                
                
                while (!(modeMatch = selector.match(modeRe))) {
                    
                    
                    for (i = 0; selector && i < length; i++) {
                        matcher = matchers[i];
                        selectorMatch = selector.match(matcher.re);
                        method = matcher.method;

                        
                        
                        
                        if (selectorMatch) {
                            operations.push({
                                method: Ext.isString(matcher.method)
                                    
                                    
                                    
                                    ? Ext.functionFactory('items', Ext.String.format.apply(Ext.String, [method].concat(selectorMatch.slice(1))))
                                    : matcher.method,
                                args: selectorMatch.slice(1)
                            });
                            selector = selector.replace(selectorMatch[0], '');
                            break; 
                        }
                        
                        if (i === (length - 1)) {
                            Ext.Error.raise('Invalid ComponentQuery selector: "' + arguments[0] + '"');
                        }
                    }
                }

                
                
                
                
                if (modeMatch[1]) { 
                    operations.push({
                        mode: modeMatch[2]||modeMatch[1]
                    });
                    selector = selector.replace(modeMatch[0], '');
                }
            }

            
            
            return new cq.Query({
                operations: operations
            });
        }
    });
});

Ext.define('Ext.XTemplateParser', {
    constructor: function (config) {
        Ext.apply(this, config);
    },

    

    
    

    
    

    
    

    
    

    
    

    
    

    
    

    
    

    
    

    
    

    
    

    
    

    
    

    
    doTpl: Ext.emptyFn,

    parse: function (str) {
        var me = this,
            len = str.length,
            aliases = { elseif: 'elif' },
            topRe = me.topRe,
            actionsRe = me.actionsRe,
            index, stack, s, m, t, prev, frame, subMatch, begin, end, actions;

        me.level = 0;
        me.stack = stack = [];

        for (index = 0; index < len; index = end) {
            topRe.lastIndex = index;
            m = topRe.exec(str);

            if (!m) {
                me.doText(str.substring(index, len));
                break;
            }

            begin = m.index;
            end = topRe.lastIndex;

            if (index < begin) {
                me.doText(str.substring(index, begin));
            }

            if (m[1]) {
                end = str.indexOf('%}', begin+2);
                me.doEval(str.substring(begin+2, end));
                end += 2;
            } else if (m[2]) {
                end = str.indexOf(']}', begin+2);
                me.doExpr(str.substring(begin+2, end));
                end += 2;
            } else if (m[3]) { 
                me.doTag(m[3]);
            } else if (m[4]) {
                actions = null;
                while ((subMatch = actionsRe.exec(m[4])) !== null) {
                    s = subMatch[2] || subMatch[3];
                    if (s) {
                        s = Ext.String.htmlDecode(s); 
                        t = subMatch[1];
                        t = aliases[t] || t;
                        actions = actions || {};
                        prev = actions[t];

                        if (typeof prev == 'string') {
                            actions[t] = [prev, s];
                        } else if (prev) {
                            actions[t].push(s);
                        } else {
                            actions[t] = s;
                        }
                    }
                }

                if (!actions) {
                    if (me.elseRe.test(m[4])) {
                        me.doElse();
                    } else if (me.defaultRe.test(m[4])) {
                        me.doDefault();
                    } else {
                        me.doTpl();
                        stack.push({ type: 'tpl' });
                    }
                }
                else if (actions['if']) {
                    me.doIf(actions['if'], actions)
                    stack.push({ type: 'if' });
                }
                else if (actions['switch']) {
                    me.doSwitch(actions['switch'], actions)
                    stack.push({ type: 'switch' });
                }
                else if (actions['case']) {
                    me.doCase(actions['case'], actions);
                }
                else if (actions['elif']) {
                    me.doElseIf(actions['elif'], actions);
                }
                else if (actions['for']) {
                    ++me.level;
                    me.doFor(actions['for'], actions);
                    stack.push({ type: 'for', actions: actions });
                }
                else if (actions.exec) {
                    me.doExec(actions.exec, actions);
                    stack.push({ type: 'exec', actions: actions });
                }
                
            } else {
                frame = stack.pop();
                me.doEnd(frame.type, frame.actions);
                if (frame.type == 'for') {
                    --me.level;
                }
            }
        }
    },

    
    
    topRe:     /(?:(\{\%)|(\{\[)|\{([^{}]*)\})|(?:<tpl([^>]*)\>)|(?:<\/tpl>)/g,
    actionsRe: /\s*(elif|elseif|if|for|exec|switch|case|eval)\s*\=\s*(?:(?:["]([^"]*)["])|(?:[']([^']*)[']))\s*/g,
    defaultRe: /^\s*default\s*$/,
    elseRe:    /^\s*else\s*$/
});

/**
 * @author Don Griffin
 *
 * This class is a base for all id generators. It also provides lookup of id generators by
 * their id.
 * 
 * Generally, id generators are used to generate a primary key for new model instances. There
 * are different approaches to solving this problem, so this mechanism has both simple use
 * cases and is open to custom implementations. A {@link Ext.data.Model} requests id generation
 * using the {@link Ext.data.Model#idgen} property.
 *
 * # Identity, Type and Shared IdGenerators
 *
 * It is often desirable to share IdGenerators to ensure uniqueness or common configuration.
 * This is done by giving IdGenerator instances an id property by which they can be looked
 * up using the {@link #get} method. To configure two {@link Ext.data.Model Model} classes
 * to share one {@link Ext.data.SequentialIdGenerator sequential} id generator, you simply
 * assign them the same id:
 *
 *     Ext.define('MyApp.data.MyModelA', {
 *         extend: 'Ext.data.Model',
 *         idgen: {
 *             type: 'sequential',
 *             id: 'foo'
 *         }
 *     });
 *
 *     Ext.define('MyApp.data.MyModelB', {
 *         extend: 'Ext.data.Model',
 *         idgen: {
 *             type: 'sequential',
 *             id: 'foo'
 *         }
 *     });
 *
 * To make this as simple as possible for generator types that are shared by many (or all)
 * Models, the IdGenerator types (such as 'sequential' or 'uuid') are also reserved as
 * generator id's. This is used by the {@link Ext.data.UuidGenerator} which has an id equal
 * to its type ('uuid'). In other words, the following Models share the same generator:
 *
 *     Ext.define('MyApp.data.MyModelX', {
 *         extend: 'Ext.data.Model',
 *         idgen: 'uuid'
 *     });
 *
 *     Ext.define('MyApp.data.MyModelY', {
 *         extend: 'Ext.data.Model',
 *         idgen: 'uuid'
 *     });
 *
 * This can be overridden (by specifying the id explicitly), but there is no particularly
 * good reason to do so for this generator type.
 *
 * # Creating Custom Generators
 * 
 * An id generator should derive from this class and implement the {@link #generate} method.
 * The constructor will apply config properties on new instances, so a constructor is often
 * not necessary.
 *
 * To register an id generator type, a derived class should provide an `alias` like so:
 *
 *     Ext.define('MyApp.data.CustomIdGenerator', {
 *         extend: 'Ext.data.IdGenerator',
 *         alias: 'idgen.custom',
 *
 *         configProp: 42, // some config property w/default value
 *
 *         generate: function () {
 *             return ... // a new id
 *         }
 *     });
 *
 * Using the custom id generator is then straightforward:
 *
 *     Ext.define('MyApp.data.MyModel', {
 *         extend: 'Ext.data.Model',
 *         idgen: 'custom'
 *     });
 *     // or...
 *
 *     Ext.define('MyApp.data.MyModel', {
 *         extend: 'Ext.data.Model',
 *         idgen: {
 *             type: 'custom',
 *             configProp: value
 *         }
 *     });
 *
 * It is not recommended to mix shared generators with generator configuration. This leads
 * to unpredictable results unless all configurations match (which is also redundant). In
 * such cases, a custom generator with a default id is the best approach.
 *
 *     Ext.define('MyApp.data.CustomIdGenerator', {
 *         extend: 'Ext.data.SequentialIdGenerator',
 *         alias: 'idgen.custom',
 *
 *         id: 'custom', // shared by default
 *
 *         prefix: 'ID_',
 *         seed: 1000
 *     });
 *
 *     Ext.define('MyApp.data.MyModelX', {
 *         extend: 'Ext.data.Model',
 *         idgen: 'custom'
 *     });
 *
 *     Ext.define('MyApp.data.MyModelY', {
 *         extend: 'Ext.data.Model',
 *         idgen: 'custom'
 *     });
 *
 *     // the above models share a generator that produces ID_1000, ID_1001, etc..
 *
 */
Ext.define('Ext.data.IdGenerator', {

    isGenerator: true,

    /**
     * Initializes a new instance.
     * @param {Object} config (optional) Configuration object to be applied to the new instance.
     */
    constructor: function(config) {
        var me = this;

        Ext.apply(me, config);

        if (me.id) {
            Ext.data.IdGenerator.all[me.id] = me;
        }
    },

    /**
     * @cfg {String} id
     * The id by which to register a new instance. This instance can be found using the
     * {@link Ext.data.IdGenerator#get} static method.
     */

    getRecId: function (rec) {
        return rec.modelName + '-' + rec.internalId;
    },

    /**
     * Generates and returns the next id. This method must be implemented by the derived
     * class.
     *
     * @return {String} The next id.
     * @method generate
     * @abstract
     */

    statics: {
        /**
         * @property {Object} all
         * This object is keyed by id to lookup instances.
         * @private
         * @static
         */
        all: {},

        /**
         * Returns the IdGenerator given its config description.
         * @param {String/Object} config If this parameter is an IdGenerator instance, it is
         * simply returned. If this is a string, it is first used as an id for lookup and
         * then, if there is no match, as a type to create a new instance. This parameter
         * can also be a config object that contains a `type` property (among others) that
         * are used to create and configure the instance.
         * @static
         */
        get: function (config) {
            var generator,
                id,
                type;

            if (typeof config == 'string') {
                id = type = config;
                config = null;
            } else if (config.isGenerator) {
                return config;
            } else {
                id = config.id || config.type;
                type = config.type;
            }

            generator = this.all[id];
            if (!generator) {
                generator = Ext.create('idgen.' + type, config);
            }

            return generator;
        }
    }
});

/**
 * @class Ext.data.JsonP
 * @singleton
 * This class is used to create JSONP requests. JSONP is a mechanism that allows for making
 * requests for data cross domain. More information is available here:
 * http://en.wikipedia.org/wiki/JSONP
 */
Ext.define('Ext.data.JsonP', {
    
    /* Begin Definitions */
    
    singleton: true,
    
    statics: {
        requestCount: 0,
        requests: {}
    },
    
    /* End Definitions */
    
    /**
     * @property timeout
     * @type Number
     * A default timeout for any JsonP requests. If the request has not completed in this time the
     * failure callback will be fired. The timeout is in ms. Defaults to <tt>30000</tt>.
     */
    timeout: 30000,
    
    /**
     * @property disableCaching
     * @type Boolean
     * True to add a unique cache-buster param to requests. Defaults to <tt>true</tt>.
     */
    disableCaching: true,
   
    /**
     * @property disableCachingParam 
     * @type String
     * Change the parameter which is sent went disabling caching through a cache buster. Defaults to <tt>'_dc'</tt>.
     */
    disableCachingParam: '_dc',
   
    /**
     * @property callbackKey
     * @type String
     * Specifies the GET parameter that will be sent to the server containing the function name to be executed when
     * the request completes. Defaults to <tt>callback</tt>. Thus, a common request will be in the form of
     * url?callback=Ext.data.JsonP.callback1
     */
    callbackKey: 'callback',
   
    /**
     * Makes a JSONP request.
     * @param {Object} options An object which may contain the following properties. Note that options will
     * take priority over any defaults that are specified in the class.
     * <ul>
     * <li><b>url</b> : String <div class="sub-desc">The URL to request.</div></li>
     * <li><b>params</b> : Object (Optional)<div class="sub-desc">An object containing a series of
     * key value pairs that will be sent along with the request.</div></li>
     * <li><b>timeout</b> : Number (Optional) <div class="sub-desc">See {@link #timeout}</div></li>
     * <li><b>callbackKey</b> : String (Optional) <div class="sub-desc">See {@link #callbackKey}</div></li>
     * <li><b>callbackName</b> : String (Optional) <div class="sub-desc">The function name to use for this request.
     * By default this name will be auto-generated: Ext.data.JsonP.callback1, Ext.data.JsonP.callback2, etc.
     * Setting this option to "my_name" will force the function name to be Ext.data.JsonP.my_name.
     * Use this if you want deterministic behavior, but be careful - the callbackName should be different
     * in each JsonP request that you make.</div></li>
     * <li><b>disableCaching</b> : Boolean (Optional) <div class="sub-desc">See {@link #disableCaching}</div></li>
     * <li><b>disableCachingParam</b> : String (Optional) <div class="sub-desc">See {@link #disableCachingParam}</div></li>
     * <li><b>success</b> : Function (Optional) <div class="sub-desc">A function to execute if the request succeeds.</div></li>
     * <li><b>failure</b> : Function (Optional) <div class="sub-desc">A function to execute if the request fails.</div></li>
     * <li><b>callback</b> : Function (Optional) <div class="sub-desc">A function to execute when the request 
     * completes, whether it is a success or failure.</div></li>
     * <li><b>scope</b> : Object (Optional)<div class="sub-desc">The scope in
     * which to execute the callbacks: The "this" object for the callback function. Defaults to the browser window.</div></li>
     * </ul>
     * @return {Object} request An object containing the request details.
     */
    request: function(options){
        options = Ext.apply({}, options);
       
        if (!options.url) {
            Ext.Error.raise('A url must be specified for a JSONP request.');
        }
        
        var me = this, 
            disableCaching = Ext.isDefined(options.disableCaching) ? options.disableCaching : me.disableCaching, 
            cacheParam = options.disableCachingParam || me.disableCachingParam, 
            id = ++me.statics().requestCount, 
            callbackName = options.callbackName || 'callback' + id, 
            callbackKey = options.callbackKey || me.callbackKey, 
            timeout = Ext.isDefined(options.timeout) ? options.timeout : me.timeout, 
            params = Ext.apply({}, options.params), 
            url = options.url,
            request, 
            script;
            
        params[callbackKey] = 'Ext.data.JsonP.' + callbackName;
        if (disableCaching) {
            params[cacheParam] = new Date().getTime();
        }
        
        script = me.createScript(url, params);
        
        me.statics().requests[id] = request = {
            url: url,
            params: params,
            script: script,
            id: id,
            scope: options.scope,
            success: options.success,
            failure: options.failure,
            callback: options.callback,
            callbackName: callbackName
        };
        
        if (timeout > 0) {
            request.timeout = setTimeout(Ext.bind(me.handleTimeout, me, [request]), timeout);
        }
        
        me.setupErrorHandling(request);
        me[callbackName] = Ext.bind(me.handleResponse, me, [request], true);
        Ext.getHead().appendChild(script);
        return request;
    },
    
    /**
     * Abort a request. If the request parameter is not specified all open requests will
     * be aborted.
     * @param {Object/String} request (Optional) The request to abort
     */
    abort: function(request){
        var requests = this.statics().requests,
            key;
            
        if (request) {
            if (!request.id) {
                request = requests[request];
            }
            this.abort(request);
        } else {
            for (key in requests) {
                if (requests.hasOwnProperty(key)) {
                    this.abort(requests[key]);
                }
            }
        }
    },
    
    /**
     * Sets up error handling for the script
     * @private
     * @param {Object} request The request
     */
    setupErrorHandling: function(request){
        request.script.onerror = Ext.bind(this.handleError, this, [request]);
    },
    
    /**
     * Handles any aborts when loading the script
     * @private
     * @param {Object} request The request
     */
    handleAbort: function(request){
        request.errorType = 'abort';
        this.handleResponse(null, request);
    },
    
    /**
     * Handles any script errors when loading the script
     * @private
     * @param {Object} request The request
     */
    handleError: function(request){
        request.errorType = 'error';
        this.handleResponse(null, request);
    },
 
    /**
     * Cleans up anu script handling errors
     * @private
     * @param {Object} request The request
     */
    cleanupErrorHandling: function(request){
        request.script.onerror = null;
    },
 
    /**
     * Handle any script timeouts
     * @private
     * @param {Object} request The request
     */
    handleTimeout: function(request){
        request.errorType = 'timeout';
        this.handleResponse(null, request);
    },
 
    /**
     * Handle a successful response
     * @private
     * @param {Object} result The result from the request
     * @param {Object} request The request
     */
    handleResponse: function(result, request){
 
        var success = true;
 
        if (request.timeout) {
            clearTimeout(request.timeout);
        }
        delete this[request.callbackName];
        delete this.statics()[request.id];
        this.cleanupErrorHandling(request);
        Ext.fly(request.script).remove();
 
        if (request.errorType) {
            success = false;
            Ext.callback(request.failure, request.scope, [request.errorType]);
        } else {
            Ext.callback(request.success, request.scope, [result]);
        }
        Ext.callback(request.callback, request.scope, [success, result, request.errorType]);
    },
    
    /**
     * Create the script tag
     * @private
     * @param {String} url The url of the request
     * @param {Object} params Any extra params to be sent
     */
    createScript: function(url, params) {
        var script = document.createElement('script');
        script.setAttribute("src", Ext.urlAppend(url, Ext.Object.toQueryString(params)));
        script.setAttribute("async", true);
        script.setAttribute("type", "text/javascript");
        return script;
    }
});

/**
 * @author Ed Spencer
 *
 * Represents a single read or write operation performed by a {@link Ext.data.proxy.Proxy Proxy}. Operation objects are
 * used to enable communication between Stores and Proxies. Application developers should rarely need to interact with
 * Operation objects directly.
 *
 * Several Operations can be batched together in a {@link Ext.data.Batch batch}.
 */
Ext.define('Ext.data.Operation', {
    /**
     * @cfg {Boolean} synchronous
     * True if this Operation is to be executed synchronously (defaults to true). This property is inspected by a
     * {@link Ext.data.Batch Batch} to see if a series of Operations can be executed in parallel or not.
     */
    synchronous: true,

    /**
     * @cfg {String} action
     * The action being performed by this Operation. Should be one of 'create', 'read', 'update' or 'destroy'.
     */
    action: undefined,

    /**
     * @cfg {Ext.util.Filter[]} filters
     * Optional array of filter objects. Only applies to 'read' actions.
     */
    filters: undefined,

    /**
     * @cfg {Ext.util.Sorter[]} sorters
     * Optional array of sorter objects. Only applies to 'read' actions.
     */
    sorters: undefined,

    /**
     * @cfg {Ext.util.Grouper} group
     * Optional grouping configuration. Only applies to 'read' actions where grouping is desired.
     */
    group: undefined,

    /**
     * @cfg {Number} start
     * The start index (offset), used in paging when running a 'read' action.
     */
    start: undefined,

    /**
     * @cfg {Number} limit
     * The number of records to load. Used on 'read' actions when paging is being used.
     */
    limit: undefined,

    /**
     * @cfg {Ext.data.Batch} batch
     * The batch that this Operation is a part of.
     */
    batch: undefined,

    /**
     * @cfg {Function} callback
     * Function to execute when operation completed.  Will be called with the following parameters:
     *
     * - records : Array of Ext.data.Model objects.
     * - operation : The Ext.data.Operation itself.
     * - success : True when operation completed successfully.
     */
    callback: undefined,

    /**
     * @cfg {Object} scope
     * Scope for the {@link #callback} function.
     */
    scope: undefined,

    /**
     * @property {Boolean} started
     * Read-only property tracking the start status of this Operation. Use {@link #isStarted}.
     * @private
     */
    started: false,

    /**
     * @property {Boolean} running
     * Read-only property tracking the run status of this Operation. Use {@link #isRunning}.
     * @private
     */
    running: false,

    /**
     * @property {Boolean} complete
     * Read-only property tracking the completion status of this Operation. Use {@link #isComplete}.
     * @private
     */
    complete: false,

    /**
     * @property {Boolean} success
     * Read-only property tracking whether the Operation was successful or not. This starts as undefined and is set to true
     * or false by the Proxy that is executing the Operation. It is also set to false by {@link #setException}. Use
     * {@link #wasSuccessful} to query success status.
     * @private
     */
    success: undefined,

    /**
     * @property {Boolean} exception
     * Read-only property tracking the exception status of this Operation. Use {@link #hasException} and see {@link #getError}.
     * @private
     */
    exception: false,

    /**
     * @property {String/Object} error
     * The error object passed when {@link #setException} was called. This could be any object or primitive.
     * @private
     */
    error: undefined,

    /**
     * @property {RegExp} actionCommitRecordsRe
     * The RegExp used to categorize actions that require record commits. This defaults to
     * match 'create' and 'update'.
     */
    actionCommitRecordsRe: /^(?:create|update)$/i,

    /**
     * @property {RegExp} actionSkipSyncRe
     * The RegExp used to categorize actions that skip local record synchronization. This defaults
     * to match 'destroy'.
     */
    actionSkipSyncRe: /^destroy$/i,

    /**
     * Creates new Operation object.
     * @param {Object} config (optional) Config object.
     */
    constructor: function(config) {
        Ext.apply(this, config || {});
    },

    /**
     * This method is called to commit data to this instance's records given the records in
     * the server response. This is followed by calling {@link Ext.data.Model#commit} on all
     * those records (for 'create' and 'update' actions).
     *
     * If this {@link #action} is 'destroy', any server records are ignored and the
     * {@link Ext.data.Model#commit} method is not called.
     *
     * @param {Ext.data.Model[]} serverRecords An array of {@link Ext.data.Model} objects returned by
     * the server.
     * @markdown
     */
    commitRecords: function (serverRecords) {
        var me = this,
            mc, index, clientRecords, serverRec, clientRec;

        if (!me.actionSkipSyncRe.test(me.action)) {
            clientRecords = me.records;

            if (clientRecords && clientRecords.length) {
                mc = Ext.create('Ext.util.MixedCollection', true, function(r) {return r.getId();});
                mc.addAll(clientRecords);

                for (index = serverRecords ? serverRecords.length : 0; index--; ) {
                    serverRec = serverRecords[index];
                    clientRec = mc.get(serverRec.getId());

                    if (clientRec) {
                        clientRec.beginEdit();
                        clientRec.set(serverRec.data);
                        clientRec.endEdit(true);
                    }
                }

                if (me.actionCommitRecordsRe.test(me.action)) {
                    for (index = clientRecords.length; index--; ) {
                        clientRecords[index].commit();
                    }
                }
            }
        }
    },

    /**
     * Marks the Operation as started.
     */
    setStarted: function() {
        this.started = true;
        this.running = true;
    },

    /**
     * Marks the Operation as completed.
     */
    setCompleted: function() {
        this.complete = true;
        this.running  = false;
    },

    /**
     * Marks the Operation as successful.
     */
    setSuccessful: function() {
        this.success = true;
    },

    /**
     * Marks the Operation as having experienced an exception. Can be supplied with an option error message/object.
     * @param {String/Object} error (optional) error string/object
     */
    setException: function(error) {
        this.exception = true;
        this.success = false;
        this.running = false;
        this.error = error;
    },

    /**
     * Returns true if this Operation encountered an exception (see also {@link #getError})
     * @return {Boolean} True if there was an exception
     */
    hasException: function() {
        return this.exception === true;
    },

    /**
     * Returns the error string or object that was set using {@link #setException}
     * @return {String/Object} The error object
     */
    getError: function() {
        return this.error;
    },

    /**
     * Returns an array of Ext.data.Model instances as set by the Proxy.
     * @return {Ext.data.Model[]} Any loaded Records
     */
    getRecords: function() {
        var resultSet = this.getResultSet();

        return (resultSet === undefined ? this.records : resultSet.records);
    },

    /**
     * Returns the ResultSet object (if set by the Proxy). This object will contain the {@link Ext.data.Model model}
     * instances as well as meta data such as number of instances fetched, number available etc
     * @return {Ext.data.ResultSet} The ResultSet object
     */
    getResultSet: function() {
        return this.resultSet;
    },

    /**
     * Returns true if the Operation has been started. Note that the Operation may have started AND completed, see
     * {@link #isRunning} to test if the Operation is currently running.
     * @return {Boolean} True if the Operation has started
     */
    isStarted: function() {
        return this.started === true;
    },

    /**
     * Returns true if the Operation has been started but has not yet completed.
     * @return {Boolean} True if the Operation is currently running
     */
    isRunning: function() {
        return this.running === true;
    },

    /**
     * Returns true if the Operation has been completed
     * @return {Boolean} True if the Operation is complete
     */
    isComplete: function() {
        return this.complete === true;
    },

    /**
     * Returns true if the Operation has completed and was successful
     * @return {Boolean} True if successful
     */
    wasSuccessful: function() {
        return this.isComplete() && this.success === true;
    },

    /**
     * @private
     * Associates this Operation with a Batch
     * @param {Ext.data.Batch} batch The batch
     */
    setBatch: function(batch) {
        this.batch = batch;
    },

    /**
     * Checks whether this operation should cause writing to occur.
     * @return {Boolean} Whether the operation should cause a write to occur.
     */
    allowWrite: function() {
        return this.action != 'read';
    }
});
/**
 * @author Ed Spencer
 * @class Ext.data.Request
 * @extends Object
 * 
 * <p>Simple class that represents a Request that will be made by any {@link Ext.data.proxy.Server} subclass.
 * All this class does is standardize the representation of a Request as used by any ServerProxy subclass,
 * it does not contain any actual logic or perform the request itself.</p>
 * 
 */
Ext.define('Ext.data.Request', {
    /**
     * @cfg {String} action The name of the action this Request represents. Usually one of 'create', 'read', 'update' or 'destroy'
     */
    action: undefined,
    
    /**
     * @cfg {Object} params HTTP request params. The Proxy and its Writer have access to and can modify this object.
     */
    params: undefined,
    
    /**
     * @cfg {String} method The HTTP method to use on this Request (defaults to 'GET'). Should be one of 'GET', 'POST', 'PUT' or 'DELETE'
     */
    method: 'GET',
    
    /**
     * @cfg {String} url The url to access on this Request
     */
    url: undefined,

    /**
     * Creates the Request object.
     * @param {Object} config (optional) Config object.
     */
    constructor: function(config) {
        Ext.apply(this, config);
    }
});
/**
 * @author Ed Spencer
 * @class Ext.data.ResultSet
 * @extends Object
 *
 * <p>Simple wrapper class that represents a set of records returned by a Proxy.</p>
 */
Ext.define('Ext.data.ResultSet', {
    /**
     * @cfg {Boolean} loaded
     * True if the records have already been loaded. This is only meaningful when dealing with
     * SQL-backed proxies
     */
    loaded: true,

    /**
     * @cfg {Number} count
     * The number of records in this ResultSet. Note that total may differ from this number
     */
    count: 0,

    /**
     * @cfg {Number} total
     * The total number of records reported by the data source. This ResultSet may form a subset of
     * those records (see count)
     */
    total: 0,

    /**
     * @cfg {Boolean} success
     * True if the ResultSet loaded successfully, false if any errors were encountered
     */
    success: false,

    /**
     * @cfg {Ext.data.Model[]} records The array of record instances. Required
     */

    /**
     * Creates the resultSet
     * @param {Object} config (optional) Config object.
     */
    constructor: function(config) {
        Ext.apply(this, config);

        /**
         * DEPRECATED - will be removed in Ext JS 5.0. This is just a copy of this.total - use that instead
         * @property {Number} totalRecords
         */
        this.totalRecords = this.total;

        if (config.count === undefined) {
            this.count = this.records.length;
        }
    }
});
/**
 * @author Don Griffin
 *
 * This class is a sequential id generator. A simple use of this class would be like so:
 *
 *     Ext.define('MyApp.data.MyModel', {
 *         extend: 'Ext.data.Model',
 *         idgen: 'sequential'
 *     });
 *     // assign id's of 1, 2, 3, etc.
 *
 * An example of a configured generator would be:
 *
 *     Ext.define('MyApp.data.MyModel', {
 *         extend: 'Ext.data.Model',
 *         idgen: {
 *             type: 'sequential',
 *             prefix: 'ID_',
 *             seed: 1000
 *         }
 *     });
 *     // assign id's of ID_1000, ID_1001, ID_1002, etc.
 *
 */
Ext.define('Ext.data.SequentialIdGenerator', {
    extend: 'Ext.data.IdGenerator',
    alias: 'idgen.sequential',

    constructor: function() {
        var me = this;

        me.callParent(arguments);

        me.parts = [ me.prefix, ''];
    },

    /**
     * @cfg {String} prefix
     * The string to place in front of the sequential number for each generated id. The
     * default is blank.
     */
    prefix: '',

    /**
     * @cfg {Number} seed
     * The number at which to start generating sequential id's. The default is 1.
     */
    seed: 1,

    /**
     * Generates and returns the next id.
     * @return {String} The next id.
     */
    generate: function () {
        var me = this,
            parts = me.parts;

        parts[1] = me.seed++;
        return parts.join('');
    }
});

/**
 * @class Ext.data.SortTypes
 * This class defines a series of static methods that are used on a
 * {@link Ext.data.Field} for performing sorting. The methods cast the 
 * underlying values into a data type that is appropriate for sorting on
 * that particular field.  If a {@link Ext.data.Field#type} is specified, 
 * the sortType will be set to a sane default if the sortType is not 
 * explicitly defined on the field. The sortType will make any necessary
 * modifications to the value and return it.
 * <ul>
 * <li><b>asText</b> - Removes any tags and converts the value to a string</li>
 * <li><b>asUCText</b> - Removes any tags and converts the value to an uppercase string</li>
 * <li><b>asUCText</b> - Converts the value to an uppercase string</li>
 * <li><b>asDate</b> - Converts the value into Unix epoch time</li>
 * <li><b>asFloat</b> - Converts the value to a floating point number</li>
 * <li><b>asInt</b> - Converts the value to an integer number</li>
 * </ul>
 * <p>
 * It is also possible to create a custom sortType that can be used throughout
 * an application.
 * <pre><code>
Ext.apply(Ext.data.SortTypes, {
    asPerson: function(person){
        // expects an object with a first and last name property
        return person.lastName.toUpperCase() + person.firstName.toLowerCase();
    }    
});

Ext.define('Employee', {
    extend: 'Ext.data.Model',
    fields: [{
        name: 'person',
        sortType: 'asPerson'
    }, {
        name: 'salary',
        type: 'float' // sortType set to asFloat
    }]
});
 * </code></pre>
 * </p>
 * @singleton
 * @docauthor Evan Trimboli <evan@sencha.com>
 */
Ext.define('Ext.data.SortTypes', {
    
    singleton: true,
    
    /**
     * Default sort that does nothing
     * @param {Object} s The value being converted
     * @return {Object} The comparison value
     */
    none : function(s) {
        return s;
    },

    /**
     * The regular expression used to strip tags
     * @type {RegExp}
     * @property
     */
    stripTagsRE : /<\/?[^>]+>/gi,

    /**
     * Strips all HTML tags to sort on text only
     * @param {Object} s The value being converted
     * @return {String} The comparison value
     */
    asText : function(s) {
        return String(s).replace(this.stripTagsRE, "");
    },

    /**
     * Strips all HTML tags to sort on text only - Case insensitive
     * @param {Object} s The value being converted
     * @return {String} The comparison value
     */
    asUCText : function(s) {
        return String(s).toUpperCase().replace(this.stripTagsRE, "");
    },

    /**
     * Case insensitive string
     * @param {Object} s The value being converted
     * @return {String} The comparison value
     */
    asUCString : function(s) {
        return String(s).toUpperCase();
    },

    /**
     * Date sorting
     * @param {Object} s The value being converted
     * @return {Number} The comparison value
     */
    asDate : function(s) {
        if(!s){
            return 0;
        }
        if(Ext.isDate(s)){
            return s.getTime();
        }
        return Date.parse(String(s));
    },

    /**
     * Float sorting
     * @param {Object} s The value being converted
     * @return {Number} The comparison value
     */
    asFloat : function(s) {
        var val = parseFloat(String(s).replace(/,/g, ""));
        return isNaN(val) ? 0 : val;
    },

    /**
     * Integer sorting
     * @param {Object} s The value being converted
     * @return {Number} The comparison value
     */
    asInt : function(s) {
        var val = parseInt(String(s).replace(/,/g, ""), 10);
        return isNaN(val) ? 0 : val;
    }
});
/**
 * @class Ext.data.Types
 * <p>This is s static class containing the system-supplied data types which may be given to a {@link Ext.data.Field Field}.<p/>
 * <p>The properties in this class are used as type indicators in the {@link Ext.data.Field Field} class, so to
 * test whether a Field is of a certain type, compare the {@link Ext.data.Field#type type} property against properties
 * of this class.</p>
 * <p>Developers may add their own application-specific data types to this class. Definition names must be UPPERCASE.
 * each type definition must contain three properties:</p>
 * <div class="mdetail-params"><ul>
 * <li><code>convert</code> : <i>Function</i><div class="sub-desc">A function to convert raw data values from a data block into the data
 * to be stored in the Field. The function is passed the collowing parameters:
 * <div class="mdetail-params"><ul>
 * <li><b>v</b> : Mixed<div class="sub-desc">The data value as read by the Reader, if undefined will use
 * the configured <tt>{@link Ext.data.Field#defaultValue defaultValue}</tt>.</div></li>
 * <li><b>rec</b> : Mixed<div class="sub-desc">The data object containing the row as read by the Reader.
 * Depending on the Reader type, this could be an Array ({@link Ext.data.reader.Array ArrayReader}), an object
 * ({@link Ext.data.reader.Json JsonReader}), or an XML element.</div></li>
 * </ul></div></div></li>
 * <li><code>sortType</code> : <i>Function</i> <div class="sub-desc">A function to convert the stored data into comparable form, as defined by {@link Ext.data.SortTypes}.</div></li>
 * <li><code>type</code> : <i>String</i> <div class="sub-desc">A textual data type name.</div></li>
 * </ul></div>
 * <p>For example, to create a VELatLong field (See the Microsoft Bing Mapping API) containing the latitude/longitude value of a datapoint on a map from a JsonReader data block
 * which contained the properties <code>lat</code> and <code>long</code>, you would define a new data type like this:</p>
 *<pre><code>
// Add a new Field data type which stores a VELatLong object in the Record.
Ext.data.Types.VELATLONG = {
    convert: function(v, data) {
        return new VELatLong(data.lat, data.long);
    },
    sortType: function(v) {
        return v.Latitude;  // When sorting, order by latitude
    },
    type: 'VELatLong'
};
</code></pre>
 * <p>Then, when declaring a Model, use <pre><code>
var types = Ext.data.Types; // allow shorthand type access
Ext.define('Unit',
    extend: 'Ext.data.Model', 
    fields: [
        { name: 'unitName', mapping: 'UnitName' },
        { name: 'curSpeed', mapping: 'CurSpeed', type: types.INT },
        { name: 'latitude', mapping: 'lat', type: types.FLOAT },
        { name: 'position', type: types.VELATLONG }
    ]
});
</code></pre>
 * @singleton
 */
Ext.define('Ext.data.Types', {
    singleton: true,
    requires: ['Ext.data.SortTypes']
}, function() {
    var st = Ext.data.SortTypes;
    
    Ext.apply(Ext.data.Types, {
        /**
         * @property {RegExp} stripRe
         * A regular expression for stripping non-numeric characters from a numeric value. Defaults to <tt>/[\$,%]/g</tt>.
         * This should be overridden for localization.
         */
        stripRe: /[\$,%]/g,
        
        /**
         * @property {Object} AUTO
         * This data type means that no conversion is applied to the raw data before it is placed into a Record.
         */
        AUTO: {
            convert: function(v) {
                return v;
            },
            sortType: st.none,
            type: 'auto'
        },

        /**
         * @property {Object} STRING
         * This data type means that the raw data is converted into a String before it is placed into a Record.
         */
        STRING: {
            convert: function(v) {
                var defaultValue = this.useNull ? null : '';
                return (v === undefined || v === null) ? defaultValue : String(v);
            },
            sortType: st.asUCString,
            type: 'string'
        },

        /**
         * @property {Object} INT
         * This data type means that the raw data is converted into an integer before it is placed into a Record.
         * <p>The synonym <code>INTEGER</code> is equivalent.</p>
         */
        INT: {
            convert: function(v) {
                return v !== undefined && v !== null && v !== '' ?
                    parseInt(String(v).replace(Ext.data.Types.stripRe, ''), 10) : (this.useNull ? null : 0);
            },
            sortType: st.none,
            type: 'int'
        },
        
        /**
         * @property {Object} FLOAT
         * This data type means that the raw data is converted into a number before it is placed into a Record.
         * <p>The synonym <code>NUMBER</code> is equivalent.</p>
         */
        FLOAT: {
            convert: function(v) {
                return v !== undefined && v !== null && v !== '' ?
                    parseFloat(String(v).replace(Ext.data.Types.stripRe, ''), 10) : (this.useNull ? null : 0);
            },
            sortType: st.none,
            type: 'float'
        },
        
        /**
         * @property {Object} BOOL
         * <p>This data type means that the raw data is converted into a boolean before it is placed into
         * a Record. The string "true" and the number 1 are converted to boolean <code>true</code>.</p>
         * <p>The synonym <code>BOOLEAN</code> is equivalent.</p>
         */
        BOOL: {
            convert: function(v) {
                if (this.useNull && (v === undefined || v === null || v === '')) {
                    return null;
                }
                return v === true || v === 'true' || v == 1;
            },
            sortType: st.none,
            type: 'bool'
        },
        
        /**
         * @property {Object} DATE
         * This data type means that the raw data is converted into a Date before it is placed into a Record.
         * The date format is specified in the constructor of the {@link Ext.data.Field} to which this type is
         * being applied.
         */
        DATE: {
            convert: function(v) {
                var df = this.dateFormat,
                    parsed;
                    
                if (!v) {
                    return null;
                }
                if (Ext.isDate(v)) {
                    return v;
                }
                if (df) {
                    if (df == 'timestamp') {
                        return new Date(v*1000);
                    }
                    if (df == 'time') {
                        return new Date(parseInt(v, 10));
                    }
                    return Ext.Date.parse(v, df);
                }
                
                parsed = Date.parse(v);
                return parsed ? new Date(parsed) : null;
            },
            sortType: st.asDate,
            type: 'date'
        }
    });
    
    Ext.apply(Ext.data.Types, {
        /**
         * @property {Object} BOOLEAN
         * <p>This data type means that the raw data is converted into a boolean before it is placed into
         * a Record. The string "true" and the number 1 are converted to boolean <code>true</code>.</p>
         * <p>The synonym <code>BOOL</code> is equivalent.</p>
         */
        BOOLEAN: this.BOOL,
        
        /**
         * @property {Object} INTEGER
         * This data type means that the raw data is converted into an integer before it is placed into a Record.
         * <p>The synonym <code>INT</code> is equivalent.</p>
         */
        INTEGER: this.INT,
        
        /**
         * @property {Object} NUMBER
         * This data type means that the raw data is converted into a number before it is placed into a Record.
         * <p>The synonym <code>FLOAT</code> is equivalent.</p>
         */
        NUMBER: this.FLOAT    
    });
});

/**
 * @extend Ext.data.IdGenerator
 * @author Don Griffin
 *
 * This class generates UUID's according to RFC 4122. This class has a default id property.
 * This means that a single instance is shared unless the id property is overridden. Thus,
 * two {@link Ext.data.Model} instances configured like the following share one generator:
 *
 *     Ext.define('MyApp.data.MyModelX', {
 *         extend: 'Ext.data.Model',
 *         idgen: 'uuid'
 *     });
 *
 *     Ext.define('MyApp.data.MyModelY', {
 *         extend: 'Ext.data.Model',
 *         idgen: 'uuid'
 *     });
 *
 * This allows all models using this class to share a commonly configured instance.
 *
 * # Using Version 1 ("Sequential") UUID's
 *
 * If a server can provide a proper timestamp and a "cryptographic quality random number"
 * (as described in RFC 4122), the shared instance can be configured as follows:
 *
 *     Ext.data.IdGenerator.get('uuid').reconfigure({
 *         version: 1,
 *         clockSeq: clock, // 14 random bits
 *         salt: salt,      // 48 secure random bits (the Node field)
 *         timestamp: ts    // timestamp per Section 4.1.4
 *     });
 *
 *     // or these values can be split into 32-bit chunks:
 *
 *     Ext.data.IdGenerator.get('uuid').reconfigure({
 *         version: 1,
 *         clockSeq: clock,
 *         salt: { lo: saltLow32, hi: saltHigh32 },
 *         timestamp: { lo: timestampLow32, hi: timestamptHigh32 }
 *     });
 *
 * This approach improves the generator's uniqueness by providing a valid timestamp and
 * higher quality random data. Version 1 UUID's should not be used unless this information
 * can be provided by a server and care should be taken to avoid caching of this data.
 *
 * See http://www.ietf.org/rfc/rfc4122.txt for details.
 */
Ext.define('Ext.data.UuidGenerator', function () {
    var twoPow14 = Math.pow(2, 14),
        twoPow16 = Math.pow(2, 16),
        twoPow28 = Math.pow(2, 28),
        twoPow32 = Math.pow(2, 32);

    function toHex (value, length) {
        var ret = value.toString(16);
        if (ret.length > length) {
            ret = ret.substring(ret.length - length); // right-most digits
        } else if (ret.length < length) {
            ret = Ext.String.leftPad(ret, length, '0');
        }
        return ret;
    }

    function rand (lo, hi) {
        var v = Math.random() * (hi - lo + 1);
        return Math.floor(v) + lo;
    }

    function split (bignum) {
        if (typeof(bignum) == 'number') {
            var hi = Math.floor(bignum / twoPow32);
            return {
                lo: Math.floor(bignum - hi * twoPow32),
                hi: hi
            };
        }
        return bignum;
    }

    return {
        extend: 'Ext.data.IdGenerator',

        alias: 'idgen.uuid',

        id: 'uuid', // shared by default

        /**
         * @property {Number/Object} salt
         * When created, this value is a 48-bit number. For computation, this value is split
         * into 32-bit parts and stored in an object with `hi` and `lo` properties.
         */

        /**
         * @property {Number/Object} timestamp
         * When created, this value is a 60-bit number. For computation, this value is split
         * into 32-bit parts and stored in an object with `hi` and `lo` properties.
         */

        /**
         * @cfg {Number} version
         * The Version of UUID. Supported values are:
         *
         *  * 1 : Time-based, "sequential" UUID.
         *  * 4 : Pseudo-random UUID.
         *
         * The default is 4.
         */
        version: 4,

        constructor: function() {
            var me = this;

            me.callParent(arguments);

            me.parts = [];
            me.init();
        },

        generate: function () {
            var me = this,
                parts = me.parts,
                ts = me.timestamp;

            /*
               The magic decoder ring (derived from RFC 4122 Section 4.2.2):

               +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
               |                          time_low                             |
               +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
               |           time_mid            |  ver  |        time_hi        |
               +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
               |res|  clock_hi |   clock_low   |    salt 0   |M|     salt 1    |
               +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
               |                         salt (2-5)                            |
               +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

                         time_mid      clock_hi (low 6 bits)
                time_low     | time_hi |clock_lo
                    |        |     |   || salt[0]
                    |        |     |   ||   | salt[1..5]
                    v        v     v   vv   v v
                    0badf00d-aced-1def-b123-dfad0badbeef
                                  ^    ^     ^
                            version    |     multicast (low bit)
                                       |
                                    reserved (upper 2 bits)
            */
            parts[0] = toHex(ts.lo, 8);
            parts[1] = toHex(ts.hi & 0xFFFF, 4);
            parts[2] = toHex(((ts.hi >>> 16) & 0xFFF) | (me.version << 12), 4);
            parts[3] = toHex(0x80 | ((me.clockSeq >>> 8) & 0x3F), 2) +
                       toHex(me.clockSeq & 0xFF, 2);
            parts[4] = toHex(me.salt.hi, 4) + toHex(me.salt.lo, 8);

            if (me.version == 4) {
                me.init(); // just regenerate all the random values...
            } else {
                // sequentially increment the timestamp...
                ++ts.lo;
                if (ts.lo >= twoPow32) { // if (overflow)
                    ts.lo = 0;
                    ++ts.hi;
                }
            }

            return parts.join('-').toLowerCase();
        },

        getRecId: function (rec) {
            return rec.getId();
        },

        /**
         * @private
         */
        init: function () {
            var me = this,
                salt, time;

            if (me.version == 4) {
                // See RFC 4122 (Secion 4.4)
                //   o  If the state was unavailable (e.g., non-existent or corrupted),
                //      or the saved node ID is different than the current node ID,
                //      generate a random clock sequence value.
                me.clockSeq = rand(0, twoPow14-1);

                // we run this on every id generation...
                salt = me.salt || (me.salt = {});
                time = me.timestamp || (me.timestamp = {});

                // See RFC 4122 (Secion 4.4)
                salt.lo = rand(0, twoPow32-1);
                salt.hi = rand(0, twoPow16-1);
                time.lo = rand(0, twoPow32-1);
                time.hi = rand(0, twoPow28-1);
            } else {
                // this is run only once per-instance
                me.salt = split(me.salt);
                me.timestamp = split(me.timestamp);

                // Set multicast bit: "the least significant bit of the first octet of the
                
                me.salt.hi |= 0x100;
            }
        },

        
        reconfigure: function (config) {
            Ext.apply(this, config);
            this.init();
        }
    };
}());


Ext.define('Ext.data.validations', {
    singleton: true,
    
    
    presenceMessage: 'must be present',
    
    
    lengthMessage: 'is the wrong length',
    
    
    formatMessage: 'is the wrong format',
    
    
    inclusionMessage: 'is not included in the list of acceptable values',
    
    
    exclusionMessage: 'is not an acceptable value',
    
    
    emailMessage: 'is not a valid email address',
    
    
    emailRe: /^([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/,
    
    
    presence: function(config, value) {
        if (value === undefined) {
            value = config;
        }
        
        
        return !!value || value === 0;
    },
    
    
    length: function(config, value) {
        if (value === undefined || value === null) {
            return false;
        }
        
        var length = value.length,
            min    = config.min,
            max    = config.max;
        
        if ((min && length < min) || (max && length > max)) {
            return false;
        } else {
            return true;
        }
    },
    
    
    email: function(config, email) {
        return Ext.data.validations.emailRe.test(email);
    },
    
    
    format: function(config, value) {
        return !!(config.matcher && config.matcher.test(value));
    },
    
    
    inclusion: function(config, value) {
        return config.list && Ext.Array.indexOf(config.list,value) != -1;
    },
    
    
    exclusion: function(config, value) {
        return config.list && Ext.Array.indexOf(config.list,value) == -1;
    }
});

Ext.define('Ext.data.association.Association', {
    alternateClassName: 'Ext.data.Association',
    
    

    

    
    primaryKey: 'id',

    
    
    

    defaultReaderType: 'json',

    statics: {
        create: function(association){
            if (!association.isAssociation) {
                if (Ext.isString(association)) {
                    association = {
                        type: association
                    };
                }

                switch (association.type) {
                    case 'belongsTo':
                        return Ext.create('Ext.data.association.BelongsTo', association);
                    case 'hasMany':
                        return Ext.create('Ext.data.association.HasMany', association);
                    case 'hasOne':
                        return Ext.create('Ext.data.association.HasOne', association);
                    


                    default:
                        Ext.Error.raise('Unknown Association type: "' + association.type + '"');
                }
            }
            return association;
        }
    },

    
    constructor: function(config) {
        Ext.apply(this, config);

        var types           = Ext.ModelManager.types,
            ownerName       = config.ownerModel,
            associatedName  = config.associatedModel,
            ownerModel      = types[ownerName],
            associatedModel = types[associatedName],
            ownerProto;

        if (ownerModel === undefined) {
            Ext.Error.raise("The configured ownerModel was not valid (you tried " + ownerName + ")");
        }
        if (associatedModel === undefined) {
            Ext.Error.raise("The configured associatedModel was not valid (you tried " + associatedName + ")");
        }

        this.ownerModel = ownerModel;
        this.associatedModel = associatedModel;

        

        

        Ext.applyIf(this, {
            ownerName : ownerName,
            associatedName: associatedName
        });
    },

    
    getReader: function(){
        var me = this,
            reader = me.reader,
            model = me.associatedModel;

        if (reader) {
            if (Ext.isString(reader)) {
                reader = {
                    type: reader
                };
            }
            if (reader.isReader) {
                reader.setModel(model);
            } else {
                Ext.applyIf(reader, {
                    model: model,
                    type : me.defaultReaderType
                });
            }
            me.reader = Ext.createByAlias('reader.' + reader.type, reader);
        }
        return me.reader || null;
    }
});


Ext.define('Ext.data.association.BelongsTo', {
    extend: 'Ext.data.association.Association',
    alternateClassName: 'Ext.data.BelongsToAssociation',
    alias: 'association.belongsto',

    

    

    

    
    constructor: function(config) {
        this.callParent(arguments);

        var me             = this,
            ownerProto     = me.ownerModel.prototype,
            associatedName = me.associatedName,
            getterName     = me.getterName || 'get' + associatedName,
            setterName     = me.setterName || 'set' + associatedName;

        Ext.applyIf(me, {
            name        : associatedName,
            foreignKey  : associatedName.toLowerCase() + "_id",
            instanceName: associatedName + 'BelongsToInstance',
            associationKey: associatedName.toLowerCase()
        });

        ownerProto[getterName] = me.createGetter();
        ownerProto[setterName] = me.createSetter();
    },

    
    createSetter: function() {
        var me = this,
            foreignKey = me.foreignKey;

        
        return function(value, options, scope) {
            
            if (value && value.isModel) {
                value = value.getId();
            }
            this.set(foreignKey, value);

            if (Ext.isFunction(options)) {
                options = {
                    callback: options,
                    scope: scope || this
                };
            }

            if (Ext.isObject(options)) {
                return this.save(options);
            }
        };
    },

    
    createGetter: function() {
        var me              = this,
            associatedName  = me.associatedName,
            associatedModel = me.associatedModel,
            foreignKey      = me.foreignKey,
            primaryKey      = me.primaryKey,
            instanceName    = me.instanceName;

        
        return function(options, scope) {
            options = options || {};

            var model = this,
                foreignKeyId = model.get(foreignKey),
                success,
                instance,
                args;

            if (options.reload === true || model[instanceName] === undefined) {
                instance = Ext.ModelManager.create({}, associatedName);
                instance.set(primaryKey, foreignKeyId);

                if (typeof options == 'function') {
                    options = {
                        callback: options,
                        scope: scope || model
                    };
                }
                
                
                success = options.success;
                options.success = function(rec){
                    model[instanceName] = rec;
                    if (success) {
                        success.call(this, arguments);
                    }
                };

                associatedModel.load(foreignKeyId, options);
                
                model[instanceName] = instance;
                return instance;
            } else {
                instance = model[instanceName];
                args = [instance];
                scope = scope || model;

                
                
                
                Ext.callback(options, scope, args);
                Ext.callback(options.success, scope, args);
                Ext.callback(options.failure, scope, args);
                Ext.callback(options.callback, scope, args);

                return instance;
            }
        };
    },

    
    read: function(record, reader, associationData){
        record[this.instanceName] = reader.read([associationData]).records[0];
    }
});


Ext.define('Ext.data.association.HasOne', {
    extend: 'Ext.data.association.Association',
    alternameClassName: 'Ext.data.HasOneAssociation',

    alias: 'association.hasone',
    
    

    

    

    
    
    constructor: function(config) {
        this.callParent(arguments);

        var me             = this,
            ownerProto     = me.ownerModel.prototype,
            associatedName = me.associatedName,
            getterName     = me.getterName || 'get' + associatedName,
            setterName     = me.setterName || 'set' + associatedName;

        Ext.applyIf(me, {
            name        : associatedName,
            foreignKey  : associatedName.toLowerCase() + "_id",
            instanceName: associatedName + 'HasOneInstance',
            associationKey: associatedName.toLowerCase()
        });

        ownerProto[getterName] = me.createGetter();
        ownerProto[setterName] = me.createSetter();
    },
    
    
    createSetter: function() {
        var me              = this,
            ownerModel      = me.ownerModel,
            foreignKey      = me.foreignKey;

        
        return function(value, options, scope) {
            if (value && value.isModel) {
                value = value.getId();
            }
            
            this.set(foreignKey, value);

            if (Ext.isFunction(options)) {
                options = {
                    callback: options,
                    scope: scope || this
                };
            }

            if (Ext.isObject(options)) {
                return this.save(options);
            }
        };
    },

    
    createGetter: function() {
        var me              = this,
            ownerModel      = me.ownerModel,
            associatedName  = me.associatedName,
            associatedModel = me.associatedModel,
            foreignKey      = me.foreignKey,
            primaryKey      = me.primaryKey,
            instanceName    = me.instanceName;

        
        return function(options, scope) {
            options = options || {};

            var model = this,
                foreignKeyId = model.get(foreignKey),
                success,
                instance,
                args;

            if (options.reload === true || model[instanceName] === undefined) {
                instance = Ext.ModelManager.create({}, associatedName);
                instance.set(primaryKey, foreignKeyId);

                if (typeof options == 'function') {
                    options = {
                        callback: options,
                        scope: scope || model
                    };
                }
                
                
                success = options.success;
                options.success = function(rec){
                    model[instanceName] = rec;
                    if (success) {
                        success.call(this, arguments);
                    }
                };

                associatedModel.load(foreignKeyId, options);
                
                model[instanceName] = instance;
                return instance;
            } else {
                instance = model[instanceName];
                args = [instance];
                scope = scope || model;

                
                
                
                Ext.callback(options, scope, args);
                Ext.callback(options.success, scope, args);
                Ext.callback(options.failure, scope, args);
                Ext.callback(options.callback, scope, args);

                return instance;
            }
        };
    },
    
    
    read: function(record, reader, associationData){
        var inverse = this.associatedModel.prototype.associations.findBy(function(assoc){
            return assoc.type === 'belongsTo' && assoc.associatedName === record.$className;
        }), newRecord = reader.read([associationData]).records[0];
        
        record[this.instanceName] = newRecord;
    
        
        if (inverse) {
            newRecord[inverse.instanceName] = record;
        }
    }
});

Ext.define('Ext.data.reader.Reader', {
    requires: ['Ext.data.ResultSet'],
    alternateClassName: ['Ext.data.Reader', 'Ext.data.DataReader'],
    
    

    
    totalProperty: 'total',

    
    successProperty: 'success',

    
    root: '',
    
    
    
    
    implicitIncludes: true,
    
    isReader: true,
    
    
    constructor: function(config) {
        var me = this;
        
        Ext.apply(me, config || {});
        me.fieldCount = 0;
        me.model = Ext.ModelManager.getModel(config.model);
        if (me.model) {
            me.buildExtractors();
        }
    },

    
    setModel: function(model, setOnProxy) {
        var me = this;
        
        me.model = Ext.ModelManager.getModel(model);
        me.buildExtractors(true);
        
        if (setOnProxy && me.proxy) {
            me.proxy.setModel(me.model, true);
        }
    },

    
    read: function(response) {
        var data = response;
        
        if (response && response.responseText) {
            data = this.getResponseData(response);
        }
        
        if (data) {
            return this.readRecords(data);
        } else {
            return this.nullResultSet;
        }
    },

    
    readRecords: function(data) {
        var me  = this;
        
        
        if (me.fieldCount !== me.getFields().length) {
            me.buildExtractors(true);
        }
        
        
        me.rawData = data;

        data = me.getData(data);

        
        
        var root    = Ext.isArray(data) ? data : me.getRoot(data),
            success = true,
            recordCount = 0,
            total, value, records, message;
            
        if (root) {
            total = root.length;
        }

        if (me.totalProperty) {
            value = parseInt(me.getTotal(data), 10);
            if (!isNaN(value)) {
                total = value;
            }
        }

        if (me.successProperty) {
            value = me.getSuccess(data);
            if (value === false || value === 'false') {
                success = false;
            }
        }
        
        if (me.messageProperty) {
            message = me.getMessage(data);
        }
        
        if (root) {
            records = me.extractData(root);
            recordCount = records.length;
        } else {
            recordCount = 0;
            records = [];
        }

        return Ext.create('Ext.data.ResultSet', {
            total  : total || recordCount,
            count  : recordCount,
            records: records,
            success: success,
            message: message
        });
    },

    
    extractData : function(root) {
        var me = this,
            values  = [],
            records = [],
            Model   = me.model,
            i       = 0,
            length  = root.length,
            idProp  = me.getIdProperty(),
            node, id, record;
            
        if (!root.length && Ext.isObject(root)) {
            root = [root];
            length = 1;
        }

        for (; i < length; i++) {
            node   = root[i];
            values = me.extractValues(node);
            id     = me.getId(node);

            
            record = new Model(values, id, node);
            records.push(record);
                
            if (me.implicitIncludes) {
                me.readAssociated(record, node);
            }
        }

        return records;
    },
    
    
    readAssociated: function(record, data) {
        var associations = record.associations.items,
            i            = 0,
            length       = associations.length,
            association, associationData, proxy, reader;
        
        for (; i < length; i++) {
            association     = associations[i];
            associationData = this.getAssociatedDataRoot(data, association.associationKey || association.name);
            
            if (associationData) {
                reader = association.getReader();
                if (!reader) {
                    proxy = association.associatedModel.proxy;
                    
                    if (proxy) {
                        reader = proxy.getReader();
                    } else {
                        reader = new this.constructor({
                            model: association.associatedName
                        });
                    }
                }
                association.read(record, reader, associationData);
            }  
        }
    },
    
    
    getAssociatedDataRoot: function(data, associationName) {
        return data[associationName];
    },
    
    getFields: function() {
        return this.model.prototype.fields.items;
    },

    
    extractValues: function(data) {
        var fields = this.getFields(),
            i      = 0,
            length = fields.length,
            output = {},
            field, value;

        for (; i < length; i++) {
            field = fields[i];
            value = this.extractorFunctions[i](data);

            output[field.name] = value;
        }

        return output;
    },

    
    getData: function(data) {
        return data;
    },

    
    getRoot: function(data) {
        return data;
    },

    
    getResponseData: function(response) {
        Ext.Error.raise("getResponseData must be implemented in the Ext.data.reader.Reader subclass");
    },

    
    onMetaChange : function(meta) {
        var fields = meta.fields,
            me = this,
            newModel;
        
        
        me.meta = meta;
        
        
        me.root = meta.root || me.root;
        me.idProperty = meta.idProperty || me.idProperty;
        me.totalProperty = meta.totalProperty || me.totalProperty;
        me.successProperty = meta.successProperty || me.successProperty;
        me.messageProperty = meta.messageProperty || me.messageProperty;
        
        if (fields) {
            if (me.model) {
                me.model.setFields(fields);
                me.setModel(me.model, true);
            }
            else {
                newModel = Ext.define("Ext.data.reader.Json-Model" + Ext.id(), {
                    extend: 'Ext.data.Model',
                    fields: fields
                });
                me.setModel(newModel, true);
            }
        }
        else {
            me.buildExtractors(true);
        }
    },
    
    
    getIdProperty: function(){
        var prop = this.idProperty;
        if (Ext.isEmpty(prop)) {
            prop = this.model.prototype.idProperty;
        }
        return prop;
    },

    
    buildExtractors: function(force) {
        var me          = this,
            idProp      = me.getIdProperty(),
            totalProp   = me.totalProperty,
            successProp = me.successProperty,
            messageProp = me.messageProperty,
            accessor;
            
        if (force === true) {
            delete me.extractorFunctions;
        }
        
        if (me.extractorFunctions) {
            return;
        }   

        
        if (totalProp) {
            me.getTotal = me.createAccessor(totalProp);
        }

        if (successProp) {
            me.getSuccess = me.createAccessor(successProp);
        }

        if (messageProp) {
            me.getMessage = me.createAccessor(messageProp);
        }

        if (idProp) {
            accessor = me.createAccessor(idProp);

            me.getId = function(record) {
                var id = accessor.call(me, record);
                return (id === undefined || id === '') ? null : id;
            };
        } else {
            me.getId = function() {
                return null;
            };
        }
        me.buildFieldExtractors();
    },

    
    buildFieldExtractors: function() {
        
        var me = this,
            fields = me.getFields(),
            ln = fields.length,
            i  = 0,
            extractorFunctions = [],
            field, map;

        for (; i < ln; i++) {
            field = fields[i];
            map   = (field.mapping !== undefined && field.mapping !== null) ? field.mapping : field.name;

            extractorFunctions.push(me.createAccessor(map));
        }
        me.fieldCount = ln;

        me.extractorFunctions = extractorFunctions;
    }
}, function() {
    Ext.apply(this, {
        
        nullResultSet: Ext.create('Ext.data.ResultSet', {
            total  : 0,
            count  : 0,
            records: [],
            success: true
        })
    });
});

Ext.define('Ext.data.reader.Xml', {
    extend: 'Ext.data.reader.Reader',
    alternateClassName: 'Ext.data.XmlReader',
    alias : 'reader.xml',

    

    
    createAccessor: function(expr) {
        var me = this;

        if (Ext.isEmpty(expr)) {
            return Ext.emptyFn;
        }

        if (Ext.isFunction(expr)) {
            return expr;
        }

        return function(root) {
            return me.getNodeValue(Ext.DomQuery.selectNode(expr, root));
        };
    },

    getNodeValue: function(node) {
        if (node && node.firstChild) {
            return node.firstChild.nodeValue;
        }
        return undefined;
    },

    
    getResponseData: function(response) {
        var xml = response.responseXML;

        if (!xml) {
            Ext.Error.raise({
                response: response,
                msg: 'XML data not found in the response'
            });
        }

        return xml;
    },

    
    getData: function(data) {
        return data.documentElement || data;
    },

    
    getRoot: function(data) {
        var nodeName = data.nodeName,
            root     = this.root;

        if (!root || (nodeName && nodeName == root)) {
            return data;
        } else if (Ext.DomQuery.isXml(data)) {
            
            
            
            return Ext.DomQuery.selectNode(root, data);
        }
    },

    
    extractData: function(root) {
        var recordName = this.record;

        if (!recordName) {
            Ext.Error.raise('Record is a required parameter');
        }

        if (recordName != root.nodeName) {
            root = Ext.DomQuery.select(recordName, root);
        } else {
            root = [root];
        }
        return this.callParent([root]);
    },

    
    getAssociatedDataRoot: function(data, associationName) {
        return Ext.DomQuery.select(associationName, data)[0];
    },

    
    readRecords: function(doc) {
        
        if (Ext.isArray(doc)) {
            doc = doc[0];
        }

        
        this.xmlData = doc;
        return this.callParent([doc]);
    }
});

Ext.define('Ext.data.writer.Writer', {
    alias: 'writer.base',
    alternateClassName: ['Ext.data.DataWriter', 'Ext.data.Writer'],
    
    
    writeAllFields: true,
    
    
    nameProperty: 'name',

    
    constructor: function(config) {
        Ext.apply(this, config);
    },

    
    write: function(request) {
        var operation = request.operation,
            records   = operation.records || [],
            len       = records.length,
            i         = 0,
            data      = [];

        for (; i < len; i++) {
            data.push(this.getRecordData(records[i]));
        }
        return this.writeRecords(request, data);
    },

    
    getRecordData: function(record) {
        var isPhantom = record.phantom === true,
            writeAll = this.writeAllFields || isPhantom,
            nameProperty = this.nameProperty,
            fields = record.fields,
            data = {},
            changes,
            name,
            field,
            key;
        
        if (writeAll) {
            fields.each(function(field){
                if (field.persist) {
                    name = field[nameProperty] || field.name;
                    data[name] = record.get(field.name);
                }
            });
        } else {
            
            changes = record.getChanges();
            for (key in changes) {
                if (changes.hasOwnProperty(key)) {
                    field = fields.get(key);
                    name = field[nameProperty] || field.name;
                    data[name] = changes[key];
                }
            }
            if (!isPhantom) {
                
                data[record.idProperty] = record.getId();
            }
        }
        return data;
    }
});


Ext.define('Ext.data.writer.Xml', {
    
    
    
    extend: 'Ext.data.writer.Writer',
    alternateClassName: 'Ext.data.XmlWriter',
    
    alias: 'writer.xml',
    
    
    
    
    documentRoot: 'xmlData',
    
    
    defaultDocumentRoot: 'xmlData',

    
    header: '',

    
    record: 'record',

    
    writeRecords: function(request, data) {
        var me = this,
            xml = [],
            i = 0,
            len = data.length,
            root = me.documentRoot,
            record = me.record,
            needsRoot = data.length !== 1,
            item,
            key;
            
        
        xml.push(me.header || '');
        
        if (!root && needsRoot) {
            root = me.defaultDocumentRoot;
        }
        
        if (root) {
            xml.push('<', root, '>');
        }
            
        for (; i < len; ++i) {
            item = data[i];
            xml.push('<', record, '>');
            for (key in item) {
                if (item.hasOwnProperty(key)) {
                    xml.push('<', key, '>', item[key], '</', key, '>');
                }
            }
            xml.push('</', record, '>');
        }
        
        if (root) {
            xml.push('</', root, '>');
        }
            
        request.xmlData = xml.join('');
        return request;
    }
});


Ext.define('Ext.direct.RemotingMethod', {
    
    constructor: function(config){
        var me = this,
            params = Ext.isDefined(config.params) ? config.params : config.len,
            name;
            
        me.name = config.name;
        me.formHandler = config.formHandler;
        if (Ext.isNumber(params)) {
            
            me.len = params;
            me.ordered = true;
        } else {
            
            me.params = [];
            Ext.each(params, function(param){
                name = Ext.isObject(param) ? param.name : param;
                me.params.push(name);
            });
        }
    },
    
    
    getCallData: function(args){
        var me = this,
            data = null,
            len  = me.len,
            params = me.params,
            callback,
            scope,
            name;
            
        if (me.ordered) {
            callback = args[len];
            scope = args[len + 1];
            if (len !== 0) {
                data = args.slice(0, len);
            }
        } else {
            data = Ext.apply({}, args[0]);
            callback = args[1];
            scope = args[2];
            
            
            for (name in data) {
                if (data.hasOwnProperty(name)) {
                    if (!Ext.Array.contains(params, name)) {
                        delete data[name];
                    }
                }
            }
        }
        
        return {
            data: data,
            callback: callback,
            scope: scope    
        };
    }
});


Ext.define('Ext.direct.Transaction', {
    
    
   
    alias: 'direct.transaction',
    alternateClassName: 'Ext.Direct.Transaction',
   
    statics: {
        TRANSACTION_ID: 0
    },
   
    

    
    constructor: function(config){
        var me = this;
        
        Ext.apply(me, config);
        me.id = ++me.self.TRANSACTION_ID;
        me.retryCount = 0;
    },
   
    send: function(){
         this.provider.queueTransaction(this);
    },

    retry: function(){
        this.retryCount++;
        this.send();
    },

    getProvider: function(){
        return this.provider;
    }
});


Ext.define('Ext.util.Bindable', {
    
    
    bindStore: function(store, initial){
        var me = this,
            oldStore = me.store;
            
        if (!initial && me.store) {
            if (store !== oldStore && oldStore.autoDestroy) {
                oldStore.destroyStore();
            } else {
                me.unbindStoreListeners(oldStore);
            }
            me.onUnbindStore(oldStore, initial);
        }
        if (store) {
            store = Ext.data.StoreManager.lookup(store);
            me.bindStoreListeners(store);
            me.onBindStore(store, initial);
        }
        me.store = store || null;
        return me;
    },
    
    
    getStore: function(){
        return this.store;
    },
    
    
    unbindStoreListeners: function(store) {
        
        var listeners = this.storeListeners;
        if (listeners) {
            store.un(listeners);
        }
    },
    
    
    bindStoreListeners: function(store) {
        
        var me = this,
            listeners = Ext.apply({}, me.getStoreListeners());
            
        if (!listeners.scope) {
            listeners.scope = me;
        }
        me.storeListeners = listeners;
        store.on(listeners);
    },
    
    
    getStoreListeners: Ext.emptyFn,
    
    
    onUnbindStore: Ext.emptyFn,
    
    
    onBindStore: Ext.emptyFn    
});


Ext.define('Ext.util.Filter', {

    

    
    

    

    
    anyMatch: false,

    
    exactMatch: false,

    
    caseSensitive: false,

    

    
    constructor: function(config) {
        Ext.apply(this, config);

        
        
        this.filter = this.filter || this.filterFn;

        if (this.filter == undefined) {
            if (this.property == undefined || this.value == undefined) {
                
                

                
            } else {
                this.filter = this.createFilterFn();
            }

            this.filterFn = this.filter;
        }
    },

    
    createFilterFn: function() {
        var me       = this,
            matcher  = me.createValueMatcher(),
            property = me.property;

        return function(item) {
            return matcher.test(me.getRoot.call(me, item)[property]);
        };
    },

    
    getRoot: function(item) {
        return this.root == undefined ? item : item[this.root];
    },

    
    createValueMatcher: function() {
        var me            = this,
            value         = me.value,
            anyMatch      = me.anyMatch,
            exactMatch    = me.exactMatch,
            caseSensitive = me.caseSensitive,
            escapeRe      = Ext.String.escapeRegex;

        if (!value.exec) { 
            value = String(value);

            if (anyMatch === true) {
                value = escapeRe(value);
            } else {
                value = '^' + escapeRe(value);
                if (exactMatch === true) {
                    value += '$';
                }
            }
            value = new RegExp(value, caseSensitive ? '' : 'i');
         }

         return value;
    }
});

Ext.define('Ext.util.Inflector', {

    

    singleton: true,

    

    
    plurals: [
        [(/(quiz)$/i),                "$1zes"  ],
        [(/^(ox)$/i),                 "$1en"   ],
        [(/([m|l])ouse$/i),           "$1ice"  ],
        [(/(matr|vert|ind)ix|ex$/i),  "$1ices" ],
        [(/(x|ch|ss|sh)$/i),          "$1es"   ],
        [(/([^aeiouy]|qu)y$/i),       "$1ies"  ],
        [(/(hive)$/i),                "$1s"    ],
        [(/(?:([^f])fe|([lr])f)$/i),  "$1$2ves"],
        [(/sis$/i),                   "ses"    ],
        [(/([ti])um$/i),              "$1a"    ],
        [(/(buffal|tomat|potat)o$/i), "$1oes"  ],
        [(/(bu)s$/i),                 "$1ses"  ],
        [(/(alias|status|sex)$/i),    "$1es"   ],
        [(/(octop|vir)us$/i),         "$1i"    ],
        [(/(ax|test)is$/i),           "$1es"   ],
        [(/^person$/),                "people" ],
        [(/^man$/),                   "men"    ],
        [(/^(child)$/),               "$1ren"  ],
        [(/s$/i),                     "s"      ],
        [(/$/),                       "s"      ]
    ],
    
    
    singulars: [
      [(/(quiz)zes$/i),                                                    "$1"     ],
      [(/(matr)ices$/i),                                                   "$1ix"   ],
      [(/(vert|ind)ices$/i),                                               "$1ex"   ],
      [(/^(ox)en/i),                                                       "$1"     ],
      [(/(alias|status)es$/i),                                             "$1"     ],
      [(/(octop|vir)i$/i),                                                 "$1us"   ],
      [(/(cris|ax|test)es$/i),                                             "$1is"   ],
      [(/(shoe)s$/i),                                                      "$1"     ],
      [(/(o)es$/i),                                                        "$1"     ],
      [(/(bus)es$/i),                                                      "$1"     ],
      [(/([m|l])ice$/i),                                                   "$1ouse" ],
      [(/(x|ch|ss|sh)es$/i),                                               "$1"     ],
      [(/(m)ovies$/i),                                                     "$1ovie" ],
      [(/(s)eries$/i),                                                     "$1eries"],
      [(/([^aeiouy]|qu)ies$/i),                                            "$1y"    ],
      [(/([lr])ves$/i),                                                    "$1f"    ],
      [(/(tive)s$/i),                                                      "$1"     ],
      [(/(hive)s$/i),                                                      "$1"     ],
      [(/([^f])ves$/i),                                                    "$1fe"   ],
      [(/(^analy)ses$/i),                                                  "$1sis"  ],
      [(/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i), "$1$2sis"],
      [(/([ti])a$/i),                                                      "$1um"   ],
      [(/(n)ews$/i),                                                       "$1ews"  ],
      [(/people$/i),                                                       "person" ],
      [(/s$/i),                                                            ""       ]
    ],
    
    
     uncountable: [
        "sheep",
        "fish",
        "series",
        "species",
        "money",
        "rice",
        "information",
        "equipment",
        "grass",
        "mud",
        "offspring",
        "deer",
        "means"
    ],
    
    
    singular: function(matcher, replacer) {
        this.singulars.unshift([matcher, replacer]);
    },
    
    
    plural: function(matcher, replacer) {
        this.plurals.unshift([matcher, replacer]);
    },
    
    
    clearSingulars: function() {
        this.singulars = [];
    },
    
    
    clearPlurals: function() {
        this.plurals = [];
    },
    
    
    isTransnumeral: function(word) {
        return Ext.Array.indexOf(this.uncountable, word) != -1;
    },

    
    pluralize: function(word) {
        if (this.isTransnumeral(word)) {
            return word;
        }

        var plurals = this.plurals,
            length  = plurals.length,
            tuple, regex, i;
        
        for (i = 0; i < length; i++) {
            tuple = plurals[i];
            regex = tuple[0];
            
            if (regex == word || (regex.test && regex.test(word))) {
                return word.replace(regex, tuple[1]);
            }
        }
        
        return word;
    },
    
    
    singularize: function(word) {
        if (this.isTransnumeral(word)) {
            return word;
        }

        var singulars = this.singulars,
            length    = singulars.length,
            tuple, regex, i;
        
        for (i = 0; i < length; i++) {
            tuple = singulars[i];
            regex = tuple[0];
            
            if (regex == word || (regex.test && regex.test(word))) {
                return word.replace(regex, tuple[1]);
            }
        }
        
        return word;
    },
    
    
    classify: function(word) {
        return Ext.String.capitalize(this.singularize(word));
    },
    
    
    ordinalize: function(number) {
        var parsed = parseInt(number, 10),
            mod10  = parsed % 10,
            mod100 = parsed % 100;
        
        
        if (11 <= mod100 && mod100 <= 13) {
            return number + "th";
        } else {
            switch(mod10) {
                case 1 : return number + "st";
                case 2 : return number + "nd";
                case 3 : return number + "rd";
                default: return number + "th";
            }
        }
    }
}, function() {
    
    var irregulars = {
            alumnus: 'alumni',
            cactus : 'cacti',
            focus  : 'foci',
            nucleus: 'nuclei',
            radius: 'radii',
            stimulus: 'stimuli',
            ellipsis: 'ellipses',
            paralysis: 'paralyses',
            oasis: 'oases',
            appendix: 'appendices',
            index: 'indexes',
            beau: 'beaux',
            bureau: 'bureaux',
            tableau: 'tableaux',
            woman: 'women',
            child: 'children',
            man: 'men',
            corpus:	'corpora',
            criterion: 'criteria',
            curriculum:	'curricula',
            genus: 'genera',
            memorandum:	'memoranda',
            phenomenon:	'phenomena',
            foot: 'feet',
            goose: 'geese',
            tooth: 'teeth',
            antenna: 'antennae',
            formula: 'formulae',
            nebula: 'nebulae',
            vertebra: 'vertebrae',
            vita: 'vitae'
        },
        singular;
    
    for (singular in irregulars) {
        this.plural(singular, irregulars[singular]);
        this.singular(irregulars[singular], singular);
    }
});

Ext.define('Ext.util.Offset', {
    statics: {
        fromObject: function(obj) {
            return new this(obj.x, obj.y);
        }
    },

    constructor: function(x, y) {
        this.x = (x != null && !isNaN(x)) ? x : 0;
        this.y = (y != null && !isNaN(y)) ? y : 0;

        return this;
    },

    copy: function() {
        return new Ext.util.Offset(this.x, this.y);
    },

    copyFrom: function(p) {
        this.x = p.x;
        this.y = p.y;
    },

    toString: function() {
        return "Offset[" + this.x + "," + this.y + "]";
    },

    equals: function(offset) {
        if(!(offset instanceof Ext.util.Offset))
            throw new Error('offset must be an instance of Ext.util.Offset');

        return (this.x == offset.x && this.y == offset.y);
    },

    round: function(to) {
        if (!isNaN(to)) {
            var factor = Math.pow(10, to);
            this.x = Math.round(this.x * factor) / factor;
            this.y = Math.round(this.y * factor) / factor;
        } else {
            this.x = Math.round(this.x);
            this.y = Math.round(this.y);
        }
    },

    isZero: function() {
        return this.x == 0 && this.y == 0;
    }
});


Ext.define('Ext.util.Region', {

    statics: {
        
        getRegion: function(el) {
            return Ext.fly(el).getPageBox(true);
        },

        
        from: function(o) {
            return new this(o.top, o.right, o.bottom, o.left);
        }
    },

    
    constructor: function(t, r, b, l) {
        var me = this;
        me.top = t;
        me[1] = t;
        me.right = r;
        me.bottom = b;
        me.left = l;
        me[0] = l;
    },

    
    contains: function(region) {
        var me = this;
        return (region.left >= me.left &&
                region.right <= me.right &&
                region.top >= me.top &&
                region.bottom <= me.bottom);

    },

    
    intersect: function(region) {
        var me = this,
            t = Math.max(me.top, region.top),
            r = Math.min(me.right, region.right),
            b = Math.min(me.bottom, region.bottom),
            l = Math.max(me.left, region.left);

        if (b > t && r > l) {
            return new Ext.util.Region(t, r, b, l);
        }
        else {
            return false;
        }
    },

    
    union: function(region) {
        var me = this,
            t = Math.min(me.top, region.top),
            r = Math.max(me.right, region.right),
            b = Math.max(me.bottom, region.bottom),
            l = Math.min(me.left, region.left);

        return new Ext.util.Region(t, r, b, l);
    },

    
    constrainTo: function(r) {
        var me = this,
            constrain = Ext.util.Numbers.constrain;
        me.top = constrain(me.top, r.top, r.bottom);
        me.bottom = constrain(me.bottom, r.top, r.bottom);
        me.left = constrain(me.left, r.left, r.right);
        me.right = constrain(me.right, r.left, r.right);
        return me;
    },

    
    adjust: function(t, r, b, l) {
        var me = this;
        me.top += t;
        me.left += l;
        me.right += r;
        me.bottom += b;
        return me;
    },

    
    getOutOfBoundOffset: function(axis, p) {
        if (!Ext.isObject(axis)) {
            if (axis == 'x') {
                return this.getOutOfBoundOffsetX(p);
            } else {
                return this.getOutOfBoundOffsetY(p);
            }
        } else {
            p = axis;
            var d = new Ext.util.Offset();
                d.x = this.getOutOfBoundOffsetX(p.x);
                d.y = this.getOutOfBoundOffsetY(p.y);
            return d;
        }

    },

    
    getOutOfBoundOffsetX: function(p) {
        if (p <= this.left) {
            return this.left - p;
        } else if (p >= this.right) {
            return this.right - p;
        }

        return 0;
    },

    
    getOutOfBoundOffsetY: function(p) {
        if (p <= this.top) {
            return this.top - p;
        } else if (p >= this.bottom) {
            return this.bottom - p;
        }

        return 0;
    },

    
    isOutOfBound: function(axis, p) {
        if (!Ext.isObject(axis)) {
            if (axis == 'x') {
                return this.isOutOfBoundX(p);
            } else {
                return this.isOutOfBoundY(p);
            }
        } else {
            p = axis;
            return (this.isOutOfBoundX(p.x) || this.isOutOfBoundY(p.y));
        }
    },

    
    isOutOfBoundX: function(p) {
        return (p < this.left || p > this.right);
    },

    
    isOutOfBoundY: function(p) {
        return (p < this.top || p > this.bottom);
    },

    
    restrict: function(axis, p, factor) {
        if (Ext.isObject(axis)) {
            var newP;

            factor = p;
            p = axis;

            if (p.copy) {
                newP = p.copy();
            }
            else {
                newP = {
                    x: p.x,
                    y: p.y
                };
            }

            newP.x = this.restrictX(p.x, factor);
            newP.y = this.restrictY(p.y, factor);
            return newP;
        } else {
            if (axis == 'x') {
                return this.restrictX(p, factor);
            } else {
                return this.restrictY(p, factor);
            }
        }
    },

    
    restrictX: function(p, factor) {
        if (!factor) {
            factor = 1;
        }

        if (p <= this.left) {
            p -= (p - this.left) * factor;
        }
        else if (p >= this.right) {
            p -= (p - this.right) * factor;
        }
        return p;
    },

    
    restrictY: function(p, factor) {
        if (!factor) {
            factor = 1;
        }

        if (p <= this.top) {
            p -= (p - this.top) * factor;
        }
        else if (p >= this.bottom) {
            p -= (p - this.bottom) * factor;
        }
        return p;
    },

    
    getSize: function() {
        return {
            width: this.right - this.left,
            height: this.bottom - this.top
        };
    },

    
    copy: function() {
        return new Ext.util.Region(this.top, this.right, this.bottom, this.left);
    },

    
    toString: function() {
        return "Region[" + this.top + "," + this.right + "," + this.bottom + "," + this.left + "]";
    },


    
    translateBy: function(offset) {
        this.left += offset.x;
        this.right += offset.x;
        this.top += offset.y;
        this.bottom += offset.y;

        return this;
    },

    
    round: function() {
        this.top = Math.round(this.top);
        this.right = Math.round(this.right);
        this.bottom = Math.round(this.bottom);
        this.left = Math.round(this.left);

        return this;
    },

    
    equals: function(region) {
        return (this.top == region.top && this.right == region.right && this.bottom == region.bottom && this.left == region.left)
    }
});


Ext.define('Ext.util.Sorter', {

    
    
    
    
    
    
    
    
    
    direction: "ASC",
    
    constructor: function(config) {
        var me = this;
        
        Ext.apply(me, config);
        
        if (me.property == undefined && me.sorterFn == undefined) {
            Ext.Error.raise("A Sorter requires either a property or a sorter function");
        }
        
        me.updateSortFunction();
    },
    
    
    createSortFunction: function(sorterFn) {
        var me        = this,
            property  = me.property,
            direction = me.direction || "ASC",
            modifier  = direction.toUpperCase() == "DESC" ? -1 : 1;
        
        
        
        return function(o1, o2) {
            return modifier * sorterFn.call(me, o1, o2);
        };
    },
    
    
    defaultSorterFn: function(o1, o2) {
        var me = this,
            transform = me.transform,
            v1 = me.getRoot(o1)[me.property],
            v2 = me.getRoot(o2)[me.property];
            
        if (transform) {
            v1 = transform(v1);
            v2 = transform(v2);
        }

        return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
    },
    
    
    getRoot: function(item) {
        return this.root == undefined ? item : item[this.root];
    },
    
    
    setDirection: function(direction) {
        var me = this;
        me.direction = direction;
        me.updateSortFunction();
    },
    
    toggle: function() {
        var me = this;
        me.direction = Ext.String.toggle(me.direction, "ASC", "DESC");
        me.updateSortFunction();
    },
    
    updateSortFunction: function() {
        var me = this;
        me.sort = me.createSortFunction(me.sorterFn || me.defaultSorterFn);
    }
});
Ext.define('Ext.TaskQueue', {
    singleton: true,

    tasks: [],

    delay: 1,

    lastTickTime: 0,

    constructor: function() {
        var me = this;

        this.tickFn = function() {
            me.tick.call(me);
            me.lastTickTime = Ext.Date.now();
            setTimeout(me.tickFn, me.delay);
        };
    },

    queue: function(fn, scope, args) {
        var item = {
            fn: fn,
            scope: scope,
            args: args,
            disabled: false
        };

        this.tasks.push(item);

        return item;
    },

    run: function() {
        this.tickFn();
    },

    tick: function() {
        var tasks = this.tasks,
            task;

        if (tasks.length > 0) {
            task = tasks.shift();

            if (task.disabled !== true) {
                task.fn.apply(task.scope, task.args);
            }
        }
    }
});

Ext.define('Ext.Validator', {
    singleton: true,

    number: function(value, name) {
        if (!name) {
            name = 'value';
        }

        if (typeof value != 'number') {
            Ext.Logger.error('Invalid ' + name + ', must be a valid number', 2);
        }
    },

    among: function(value, values, name) {
        if (!name) {
            name = 'value';
        }

        if (values.indexOf(value) === -1) {
            Ext.Logger.error('Invalid ' + name + ', must be either of these value: "' + values.join('", "') +'"', 2);
        }
    },

    element: function(value, name) {
        if (!name) {
            name = 'value';
        }

        if (typeof value != 'string' && !value.nodeType && !(value instanceof Ext.Element)) {
            Ext.Logger.error('Invalid ' + name + ', must be either a DOM element\'s id, a DOM element reference or an instance of Ext.Element', 2);
        }
    }
});

Ext.define('Ext.behavior.Behavior', {
    constructor: function(component) {
        this.component = component;

        component.on('destroy', 'onComponentDestroy', this);
    },

    onComponentDestroy: Ext.emptyFn
});


Ext.define('Ext.dom.Helper', {
    extend: 'Ext.dom.AbstractHelper',
    alternateClassName: 'Ext.DomHelper'
    
}, function() {
    Ext.ns('Ext.core');
    Ext.core.DomHelper = Ext.DomHelper = new this();
});


Ext.define('Ext.dom.Query', {
    extend: 'Ext.dom.AbstractQuery',
    alternateClassName: 'Ext.DomQuery'
}, function() {
    Ext.ns('Ext.core');
    Ext.core.DomQuery = Ext.DomQuery = new this();
    Ext.query = Ext.Function.alias(Ext.DomQuery, 'select');
});

Ext.define('Ext.event.Controller', {

    isFiring: false,

    listenerStack: null,

    constructor: function(info) {
        this.firingListeners = [];
        this.firingArguments = [];

        this.setInfo(info);

        return this;
    },

    setInfo: function(info) {
        this.info = info;
    },

    getInfo: function() {
        return this.info;
    },

    setListenerStack: function(listenerStack) {
        this.listenerStack = listenerStack;
    },

    fire: function(args, actions) {
        var listenerStack = this.listenerStack,
            firingListeners = this.firingListeners,
            firingArguments = this.firingArguments,
            push = firingListeners.push,
            beforeActions = [],
            afterActions = [],
            listeners, beforeListeners, currentListeners, afterListeners,
            i, ln, action, actionListener, fn;

        if (listenerStack) {
            listeners = listenerStack.listeners;
            beforeListeners = listeners.before;
            currentListeners = listeners.current;
            afterListeners = listeners.after;
        }

        if (!args) {
            args = [];
        }

        if (actions) {
            for (i = 0,ln = actions.length; i < ln; i++) {
                action = actions[i];
                fn = action.fn;

                actionListener = {
                    fn: fn,
                    scope: action.scope,
                    options: action.options || {},
                    isLateBinding: typeof fn == 'string'
                };

                if (action.order === 'before') {
                    beforeActions.push(actionListener);
                }
                else {
                    afterActions.push(actionListener);
                }
            }
        }

        firingListeners.length = 0;

        if (beforeListeners && beforeListeners.length > 0) {
            push.apply(firingListeners, listeners.before);
        }

        if (beforeActions.length > 0) {
            push.apply(firingListeners, beforeActions);
        }

        if (currentListeners && currentListeners.length > 0) {
            push.apply(firingListeners, listeners.current);
        }

        if (afterActions.length > 0) {
            push.apply(firingListeners, afterActions);
        }

        if (afterListeners && afterListeners.length > 0) {
            push.apply(firingListeners, listeners.after);
        }

        if (firingListeners.length < 1) {
            return this;
        }

        firingArguments.length = 0;
        firingArguments.push.apply(firingArguments, args);

        
        firingArguments.push(null, this);

        this.doFire();

        return this;
    },

    doFire: function() {
        var listenerStack = this.listenerStack,
            firingListeners = this.firingListeners,
            firingArguments = this.firingArguments,
            optionsArgumentIndex = firingArguments.length - 2,
            i, ln, listener, options, fn, firingFn,
            boundFn, isLateBinding, scope, args, result;

        this.isPausing = false;
        this.isPaused = false;
        this.isStopped = false;
        this.isFiring = true;

        for (i = 0, ln = firingListeners.length; i < ln; i++) {
            listener = firingListeners[i];
            options = listener.options;
            fn = listener.fn;
            firingFn = listener.firingFn;
            boundFn = listener.boundFn;
            isLateBinding = listener.isLateBinding;
            scope = listener.scope;

            
            if (isLateBinding && boundFn && boundFn !== scope[fn]) {
                boundFn = false;
                firingFn = false;
            }

            if (!boundFn) {
                if (isLateBinding) {
                    boundFn = scope[fn];

                    if (!boundFn) {
                        continue;
                    }
                }
                else {
                    boundFn = fn;
                }

                listener.boundFn = boundFn;
            }

            if (!firingFn) {
                firingFn = boundFn;

                if (options.buffer) {
                    firingFn = Ext.Function.createBuffered(firingFn, options.buffer, scope);
                }

                if (options.delay) {
                    firingFn = Ext.Function.createDelayed(firingFn, options.delay, scope);
                }

                listener.firingFn = firingFn;
            }

            firingArguments[optionsArgumentIndex] = options;

            args = firingArguments;

            if (options.args) {
                args = options.args.concat(args);
            }

            if (options.single === true && listenerStack) {
                listenerStack.remove(fn, scope, listener.order);
            }

            result = firingFn.apply(scope, args);

            if (result === false) {
                this.stop();
            }

            if (this.isStopped) {
                break;
            }
            else if (result && result instanceof Array) {
                firingArguments = this.firingArguments = result.concat([null, this]);
            }

            if (this.isPausing) {
                this.isPaused = true;
                firingListeners.splice(0, i + 1);
                return;
            }
        }

        this.isFiring = false;
        this.listenerStack = null;
        firingListeners.length = 0;
        firingArguments.length = 0;
        this.connectingController = null;
    },

    connect: function(controller) {
        this.connectingController = controller;
    },

    resume: function() {
        var connectingController = this.connectingController;

        this.isPausing = false;

        if (this.isPaused && this.firingListeners.length > 0) {
            this.isPaused = false;
            this.doFire();
        }

        if (connectingController) {
            connectingController.resume();
        }

        return this;
    },

    isInterrupted: function() {
        return this.isStopped || this.isPaused;
    },

    stop: function() {
        var connectingController = this.connectingController;

        this.isStopped = true;

        if (connectingController) {
            this.connectingController = null;
            connectingController.stop();
        }

        this.isFiring = false;

        this.listenerStack = null;

        return this;
    },

    pause: function() {
        var connectingController = this.connectingController;

        this.isPausing = true;

        if (connectingController) {
            connectingController.pause();
        }

        return this;
    }
});


Ext.define('Ext.event.Event', {
    alternateClassName: 'Ext.EventObject',
    isStopped: false,
    alternativeClassName: 'Ext.EventObject',

    set: function(name, value) {
        if (arguments.length === 1 && typeof name != 'string') {
            var info = name;

            for (name in info) {
                if (info.hasOwnProperty(name)) {
                    this[name] = info[name];
                }
            }
        }
        else {
            this[name] = info[name];
        }
    },

    stopEvent: function() {
        return this.stopPropagation();
    },

    stopPropagation: function() {
        this.isStopped = true;

        return this;
    }
});

Ext.define('Ext.event.ListenerStack', {

    currentOrder: 'current',

    length: 0,

    constructor: function() {
        this.listeners = {
            before: [],
            current: [],
            after: []
        };

        this.lateBindingMap = {};

        return this;
    },

    add: function(fn, scope, options, order) {
        var lateBindingMap = this.lateBindingMap,
            listeners = this.getAll(order),
            i = listeners.length,
            listener, id;

        if (typeof fn == 'string' && scope.isIdentifiable) {
            id = scope.getId();

            if (lateBindingMap[id]) {
                if (lateBindingMap[id][fn]) {
                    return false;
                }
                else {
                    lateBindingMap[id][fn] = true;
                }
            }
            else {
                lateBindingMap[id] = {};
                lateBindingMap[id][fn] = true;
            }
        }
        else {
            if (i > 0) {
                while (i--) {
                    listener = listeners[i];

                    if (listener.fn === fn && listener.scope === scope) {
                        listener.options = options;
                        return false;
                    }
                }
            }
        }

        listener = this.create(fn, scope, options, order);

        if (options && options.prepend) {
            delete options.prepend;
            listeners.unshift(listener);
        }
        else {
            listeners.push(listener);
        }

        this.length++;

        return true;
    },

    getAt: function(index, order) {
        return this.getAll(order)[index];
    },

    getAll: function(order) {
        if (!order) {
            order = this.currentOrder;
        }

        return this.listeners[order];
    },

    count: function(order) {
        return this.getAll(order).length;
    },

    create: function(fn, scope, options, order) {
        return {
            fn: fn,
            firingFn: false,
            boundFn: false,
            isLateBinding: typeof fn == 'string',
            scope: scope,
            options: options || {},
            order: order
        };
    },

    remove: function(fn, scope, order) {
        var listeners = this.getAll(order),
            i = listeners.length,
            isRemoved = false,
            lateBindingMap = this.lateBindingMap,
            listener, id;

        if (i > 0) {
            
            
            
            while (i--) {
                listener = listeners[i];

                if (listener.fn === fn && listener.scope === scope) {
                    listeners.splice(i, 1);
                    isRemoved = true;
                    this.length--;

                    if (typeof fn == 'string' && scope.isIdentifiable) {
                        id = scope.getId();

                        if (lateBindingMap[id] && lateBindingMap[id][fn]) {
                            delete lateBindingMap[id][fn];
                        }
                    }
                    break;
                }
            }
        }

        return isRemoved;
    }
});

Ext.define('Ext.event.publisher.Publisher', {
    targetType: '',

    constructor: function() {
        var handledEvents = this.handledEvents,
            handledEventsMap,
            i, ln, event;

        handledEventsMap = this.handledEventsMap = {};

        for (i = 0,ln = handledEvents.length; i < ln; i++) {
            event = handledEvents[i];

            handledEventsMap[event] = true;
        }

        return this;
    },

    handles: function(eventName) {
        var map = this.handledEventsMap;

        return !!map[eventName] || !!map['*'] || eventName === '*';
    },

    getHandledEvents: function() {
        return this.handledEvents;
    },

    setDispatcher: function(dispatcher) {
        this.dispatcher = dispatcher;
    },

    subscribe: function() {
        return false;
    },

    unsubscribe: function() {
        return false;
    },

    notify: function() {
        return false;
    },

    getTargetType: function() {
        return this.targetType;
    },

    dispatch: function(target, eventName, args) {
        this.dispatcher.doDispatchEvent(this.targetType, target, eventName, args);
    }
});


Ext.define('Ext.fx.State', {

    isAnimatable: {
        'background-color'   : true,
        'background-image'   : true,
        'background-position': true,
        'border-bottom-color': true,
        'border-bottom-width': true,
        'border-color'       : true,
        'border-left-color'  : true,
        'border-left-width'  : true,
        'border-right-color' : true,
        'border-right-width' : true,
        'border-spacing'     : true,
        'border-top-color'   : true,
        'border-top-width'   : true,
        'border-width'       : true,
        'bottom'             : true,
        'color'              : true,
        'crop'               : true,
        'font-size'          : true,
        'font-weight'        : true,
        'height'             : true,
        'left'               : true,
        'letter-spacing'     : true,
        'line-height'        : true,
        'margin-bottom'      : true,
        'margin-left'        : true,
        'margin-right'       : true,
        'margin-top'         : true,
        'max-height'         : true,
        'max-width'          : true,
        'min-height'         : true,
        'min-width'          : true,
        'opacity'            : true,
        'outline-color'      : true,
        'outline-offset'     : true,
        'outline-width'      : true,
        'padding-bottom'     : true,
        'padding-left'       : true,
        'padding-right'      : true,
        'padding-top'        : true,
        'right'              : true,
        'text-indent'        : true,
        'text-shadow'        : true,
        'top'                : true,
        'vertical-align'     : true,
        'visibility'         : true,
        'width'              : true,
        'word-spacing'       : true,
        'z-index'            : true,
        'zoom'               : true,
        'transform'          : true
    },

    constructor: function(data) {
        this.data = {};

        this.set(data);
    },

    setConfig: function(data) {
        this.set(data);

        return this;
    },

    setRaw: function(data) {
        this.data = data;

        return this;
    },

    clear: function() {
        return this.setRaw({});
    },

    setTransform: function(name, value) {
        var data = this.data,
            isArray = Ext.isArray(value),
            transform = data.transform,
            ln, key;

        if (!transform) {
            transform = data.transform = {
                translateX: 0,
                translateY: 0,
                translateZ: 0,
                scaleX: 1,
                scaleY: 1,
                scaleZ: 1,
                rotate: 0,
                rotateX: 0,
                rotateY: 0,
                rotateZ: 0,
                skewX: 0,
                skewY: 0
            };
        }

        if (typeof name == 'string') {
            switch (name) {
                case 'translate':
                    if (isArray) {
                        ln = value.length;

                        if (ln == 0) { break; }

                        transform.translateX = value[0];

                        if (ln == 1) { break; }

                        transform.translateY = value[1];

                        if (ln == 2) { break; }

                        transform.translateZ = value[2];
                    }
                    else {
                        transform.translateX = value;
                    }
                    break;

                case 'rotate':
                    if (isArray) {
                        ln = value.length;

                        if (ln == 0) { break; }

                        transform.rotateX = value[0];

                        if (ln == 1) { break; }

                        transform.rotateY = value[1];

                        if (ln == 2) { break; }

                        transform.rotateZ = value[2];
                    }
                    else {
                        transform.rotate = value;
                    }
                    break;


                case 'scale':
                    if (isArray) {
                        ln = value.length;

                        if (ln == 0) { break; }

                        transform.scaleX = value[0];

                        if (ln == 1) { break; }

                        transform.scaleY = value[1];

                        if (ln == 2) { break; }

                        transform.scaleZ = value[2];
                    }
                    else {
                        transform.scaleX = value;
                        transform.scaleY = value;
                    }
                    break;

                case 'skew':
                    if (isArray) {
                        ln = value.length;

                        if (ln == 0) { break; }

                        transform.skewX = value[0];

                        if (ln == 1) { break; }

                        transform.skewY = value[1];
                    }
                    else {
                        transform.skewX = value;
                    }
                    break;

                default:
                    transform[name] = value;
            }
        }
        else {
            for (key in name) {
                if (name.hasOwnProperty(key)) {
                    value = name[key];

                    this.setTransform(key, value);
                }
            }
        }
    },

    set: function(name, value) {
        var data = this.data,
            key;

        if (typeof name != 'string') {
            for (key in name) {
                value = name[key];

                if (key === 'transform') {
                    this.setTransform(value);
                }
                else {
                    data[key] = value;
                }
            }
        }
        else {
            if (name === 'transform') {
                this.setTransform(value);
            }
            else {
                data[name] = value;
            }
        }

        return this;
    },

    unset: function(name) {
        var data = this.data;

        if (data.hasOwnProperty(name)) {
            delete data[name];
        }

        return this;
    },

    getData: function() {
        return this.data;
    }
});




Ext.define('Ext.fx.animation.Abstract', {

    requires: [
        'Ext.fx.State'
    ],

    config: {
        name: '',

        element: null,

        before: null,

        from: {},

        to: {},

        after: null,

        states: {},

        duration:  300,

        easing: 'linear',

        iteration: 1,

        direction: 'normal',

        delay: 0,

        onEnd: null,

        onBeforeEnd: null,

        scope: null,

        preserveEndState: true
    },

    STATE_FROM: '0%',

    STATE_TO: '100%',

    DIRECTION_UP: 'up',

    DIRECTION_DOWN: 'down',

    DIRECTION_LEFT: 'left',

    DIRECTION_RIGHT: 'right',

    stateNameRegex: /^(?:[\d\.]+)%$/,

    constructor: function(config) {
        this.states = {};

        this.initConfig(config);

        return this;
    },

    applyElement: function(element) {
        return Ext.get(element);
    },

    applyBefore: function(before, current) {
        if (before) {
            return Ext.factory(before, Ext.fx.State, current);
        }
    },

    applyAfter: function(after, current) {
        if (after) {
            return Ext.factory(after, Ext.fx.State, current);
        }
    },

    setFrom: function(from) {
        return this.setState(this.STATE_FROM, from);
    },

    setTo: function(to) {
        return this.setState(this.STATE_TO, to);
    },

    getFrom: function() {
        return this.getState(this.STATE_FROM);
    },

    getTo: function() {
        return this.getState(this.STATE_TO);
    },

    setStates: function(states) {
        var validNameRegex = this.stateNameRegex,
            name;

        for (name in states) {
            if (validNameRegex.test(name)) {
                this.setState(name, states[name]);
            }
        }

        return this;
    },

    getStates: function() {
        return this.states;
    },

    setState: function(name, state) {
        var states = this.getStates(),
            stateInstance;

        stateInstance = Ext.factory(state, Ext.fx.State, states[name]);

        if (stateInstance) {
            states[name] = stateInstance;
        }
        else if (name === this.STATE_TO) {
            Ext.Logger.error("Setting and invalid '100%' / 'to' state of: " + state);
        }

        return this;
    },

    getState: function(name) {
        return this.getStates()[name];
    },

    getData: function() {
        var states = this.getStates(),
            statesData = {},
            before = this.getBefore(),
            after = this.getAfter(),
            from = states[this.STATE_FROM],
            to = states[this.STATE_TO],
            fromData = from.getData(),
            toData = to.getData(),
            data, name, state;

        for (name in states) {
            if (states.hasOwnProperty(name)) {
                state = states[name];
                data = state.getData();
                statesData[name] = data;
            }
        }

        if (Ext.os.is.Android2) {
            statesData['0.0001%'] = fromData;
        }

        return {
            before: before ? before.getData() : {},
            after: after ? after.getData() : {},
            states: statesData,
            from: fromData,
            to: toData,
            duration: this.getDuration(),
            iteration: this.getIteration(),
            direction: this.getDirection(),
            easing: this.getEasing(),
            delay: this.getDelay(),
            onEnd: this.getOnEnd(),
            onBeforeEnd: this.getOnBeforeEnd(),
            scope: this.getScope(),
            preserveEndState: this.getPreserveEndState()
        };
    }
});


Ext.define('Ext.fx.animation.Cube', {
    extend: 'Ext.fx.animation.Abstract',

    alias: 'animation.cube',

    config: {
        
        before: {
            'transform-style': 'preserve-3d'
        },

        
        direction: 'right',

        out: false
    },

    getData: function() {
        var to = this.getTo(),
            from = this.getFrom(),
            out  = this.getOut(),
            direction  = this.getDirection(),
            el = this.getElement(),
            elW = el.getWidth(),
            elH = el.getHeight(),
            halfWidth = (elW / 2),
            halfHeight = (elH / 2),
            fromTransform = {},
            toTransform = {},
            originalFromTransform = {
                rotateY: 0,
                translateX: 0,
                translateZ: 0
            },
            originalToTransform = {
                rotateY: 90,
                translateX: halfWidth,
                translateZ: halfWidth
            },
            originalVerticalFromTransform = {
                rotateX: 0,
                translateY: 0,
                translateZ: 0
            },
            originalVerticalToTransform = {
                rotateX: 90,
                translateY: halfHeight,
                translateZ: halfHeight
            },
            tempTransform;

        if (direction == "left" || direction == "right") {
            if (out) {
                toTransform = originalToTransform;
                fromTransform = originalFromTransform;
            } else {
                toTransform = originalFromTransform;
                fromTransform = originalToTransform;
                fromTransform.rotateY *= -1;
                fromTransform.translateX *= -1;
            }

            if (direction === 'right') {
                tempTransform = fromTransform;
                fromTransform = toTransform;
                toTransform = tempTransform;
            }
        }

        if (direction == "up" || direction == "down") {
            if (out) {
                toTransform = originalVerticalFromTransform;
                fromTransform = {
                    rotateX: -90,
                    translateY: halfHeight,
                    translateZ: halfHeight
                };
            } else {
                fromTransform = originalVerticalFromTransform;
                toTransform = {
                    rotateX: 90,
                    translateY: -halfHeight,
                    translateZ: halfHeight
                };
            }

            if (direction == "up") {
                tempTransform = fromTransform;
                fromTransform = toTransform;
                toTransform = tempTransform;
            }
        }

        from.set('transform', fromTransform);
        to.set('transform', toTransform);

        return this.callParent(arguments);
    }
});


Ext.define('Ext.fx.animation.Fade', {
    extend: 'Ext.fx.animation.Abstract',

    alternateClassName: 'Ext.fx.animation.FadeIn',

    alias: 'animation.fade',

    config: {
        
        out: false,
        
        reverse: null
    },

    updateOut: function(newOut) {
        var to   = this.getTo(),
            from = this.getFrom();

        if (newOut) {
            from.set('opacity', 1);
            to.set('opacity',   0);
        } else {
            from.set('opacity', 0);
            to.set('opacity',   1);
        }
    }
});


Ext.define('Ext.fx.animation.FadeOut', {
    extend: 'Ext.fx.animation.Fade',

    config: {
        
        out: true
    }
});


Ext.define('Ext.fx.animation.Flip', {
    extend: 'Ext.fx.animation.Abstract',

    alias: 'animation.flip',

    config: {
        easing: 'ease-in',

        
        direction: 'right',

        half: false,

        out: null
    },

    getData: function() {
        var from = this.getFrom(),
            to = this.getTo(),
            direction = this.getDirection(),
            out = this.getOut(),
            half = this.getHalf(),
            rotate = (half) ? 90 : 180,
            fromRotateX = 0,
            fromRotateY = 0,
            toRotateX = 0,
            toRotateY = 0;

        switch (direction) {
            case this.DIRECTION_UP:
                if (out) {
                    toRotateX = rotate;
                }
                else {
                    fromRotateX = -rotate;
                }
                break;

            case this.DIRECTION_DOWN:
                if (out) {
                    toRotateX = -rotate;
                }
                else {
                    fromRotateX = rotate;
                }
                break;

            case this.DIRECTION_RIGHT:
                if (out) {
                    toRotateY = -rotate;
                }
                else {
                    fromRotateY = rotate;
                }
                break;

            case this.DIRECTION_LEFT:
                if (out) {
                    toRotateY = -rotate;
                }
                else {
                    fromRotateY = rotate;
                }
                break;
        }

        from.setTransform({
            rotateX: fromRotateX,
            rotateY: fromRotateY
        });

        to.setTransform({
            rotateX: toRotateX,
            rotateY: toRotateY
        });

        return this.callParent(arguments);
    }
});


Ext.define('Ext.fx.animation.Pop', {
    extend: 'Ext.fx.animation.Abstract',

    alias: 'animation.pop',

    alternateClassName: 'Ext.fx.animation.PopIn',

    config: {
        
        out: false
    },

    getData: function() {
        var to = this.getTo(),
            from = this.getFrom(),
            out = this.getOut();

        if (out) {
            from.set('opacity', 1);
            from.setTransform({
                scale: 1
            });

            to.set('opacity', 0);
            to.setTransform({
                scale: 0
            });
        }
        else {
            from.set('opacity', 0);
            from.setTransform({
                scale: 0
            });

            to.set('opacity', 1);
            to.setTransform({
                scale: 1
            });
        }

        return this.callParent(arguments);
    }
});


Ext.define('Ext.fx.animation.PopOut', {
    extend: 'Ext.fx.animation.Pop',

    config: {
        
        out: true
    }
});


Ext.define('Ext.fx.animation.Slide', {

    extend: 'Ext.fx.animation.Abstract',

    alternateClassName: 'Ext.fx.animation.SlideIn',

    alias: 'animation.slide',

    config: {
        
        direction: 'right',

        
        out: false,

        
        offset: 0,

        
        easing: 'auto',

        containerBox: 'auto',

        elementBox: 'auto',

        useCssTransform: true,

        reverse: null
    },

    reverseDirectionMap: {
        up: 'down',
        down: 'up',
        left: 'right',
        right: 'left'
    },

    applyEasing: function(easing) {
        if (easing === 'auto') {
            return 'ease-' + ((this.getOut()) ? 'in' : 'out');
        }

        return easing;
    },

    getContainerBox: function() {
        var box = this._containerBox;

        if (box === 'auto') {
            box = this.getElement().getParent().getPageBox();
        }

        return box;
    },

    getElementBox: function() {
        var box = this._elementBox;

        if (box === 'auto') {
            box = this.getElement().getPageBox();
        }

        return box;
    },

    getData: function() {
        var elementBox = this.getElementBox(),
            containerBox = this.getContainerBox(),
            box = elementBox ? elementBox : containerBox,
            from = this.getFrom(),
            to = this.getTo(),
            out = this.getOut(),
            offset = this.getOffset(),
            direction = this.getDirection(),
            useCssTransform = this.getUseCssTransform(),
            reverse = this.getReverse(),
            translateX = 0,
            translateY = 0,
            fromX, fromY, toX, toY;

        if (reverse) {
            direction = this.reverseDirectionMap[direction];
        }

        switch (direction) {
            case this.DIRECTION_UP:
                if (out) {
                    translateY = containerBox.top - box.top - box.height - offset;
                }
                else {
                    translateY = containerBox.bottom - box.bottom + box.height + offset;
                }

                break;

            case this.DIRECTION_DOWN:
                if (out) {
                    translateY = containerBox.bottom - box.bottom + box.height + offset;
                }
                else {
                    translateY = containerBox.top - box.height - box.top - offset;
                }

                break;

            case this.DIRECTION_RIGHT:
                if (out) {
                    translateX = containerBox.right - box.right + box.width + offset;
                }
                else {
                    translateX = containerBox.left - box.left - box.width - offset;
                }

                break;

            case this.DIRECTION_LEFT:
                if (out) {
                    translateX = containerBox.left - box.left - box.width - offset;
                }
                else {
                    translateX = containerBox.right - box.right + box.width + offset;
                }

                break;
        }

        fromX = (out) ? 0 : translateX;
        fromY = (out) ? 0 : translateY;

        if (useCssTransform) {
            from.setTransform({
                translateX: fromX,
                translateY: fromY
            });
        }
        else {
            from.set('left', fromX);
            from.set('top', fromY);
        }

        toX = (out) ? translateX : 0;
        toY = (out) ? translateY : 0;

        if (useCssTransform) {
            to.setTransform({
                translateX: toX,
                translateY: toY
            });
        }
        else {
            to.set('left', toX);
            to.set('top', toY);
        }

        return this.callParent(arguments);
    }
});


Ext.define('Ext.fx.animation.SlideOut', {
    extend: 'Ext.fx.animation.Slide',

    config: {
        
        out: true
    }
});


Ext.define('Ext.fx.layout.card.Abstract', {

    config: {
        layout: null
    },

    updateLayout: function() {
        this.enable();
    },

    enable: function() {
        var layout = this.getLayout();

        if (layout) {
            layout.on(layout.eventNames.activeItemChange, 'onActiveItemChange', this);
        }
    },

    disable: function() {
        var layout = this.getLayout();

        if (layout) {
            layout.un(layout.eventNames.activeItemChange, 'onActiveItemChange', this);
        }
    },

    onActiveItemChange: Ext.emptyFn,

    destroy: function() {
        var layout = this.getLayout();

        if (layout) {
            this._layout = null;
            layout.un(layout.eventNames.activeItemChange, 'onActiveItemChange', this);
        }
    }
});

Ext.define('Ext.log.Base', {
    config: {},

    constructor: function(config) {
        this.initConfig(config);

        return this;
    }
});

(function() {
var Logger = Ext.define('Ext.log.Logger', {

    extend: 'Ext.log.Base',

    statics: {
        defaultPriority: 'info',

        priorities: {
            verbose:    0,
            info:       1,
            deprecate:  2,
            warn:       3,
            error:      4
        }
    },

    config: {
        enabled: true,
        minPriority: 'deprecate',
        writers: {}
    },

    log: function(message, priority, callerId) {
        if (!this.getEnabled()) {
            return this;
        }

        var statics = Logger,
            priorities = statics.priorities,
            priorityValue = priorities[priority],
            caller = this.log.caller,
            callerDisplayName = '',
            writers = this.getWriters(),
            event, i, originalCaller;

        if (!priority) {
            priority = 'info';
        }

        if (priorities[this.getMinPriority()] > priorityValue) {
            return this;
        }

        if (!callerId) {
            callerId = 1;
        }

        if (Ext.isArray(message)) {
            message = message.join(" ");
        }
        else {
            message = String(message);
        }

        if (typeof callerId == 'number') {
            i = callerId;

            do {
                i--;

                caller = caller.caller;

                if (!caller) {
                    break;
                }

                if (!originalCaller) {
                    originalCaller = caller.caller;
                }

                if (i <= 0 && caller.displayName) {
                    break;
                }
            }
            while (caller !== originalCaller);

            callerDisplayName = Ext.getDisplayName(caller);
        }
        else {
            caller = caller.caller;
            callerDisplayName = Ext.getDisplayName(callerId) + '#' + caller.$name;
        }

        event = {
            time: Ext.Date.now(),
            priority: priorityValue,
            priorityName: priority,
            message: message,
            caller: caller,
            callerDisplayName: callerDisplayName
        };

        for (i in writers) {
            if (writers.hasOwnProperty(i)) {
                writers[i].write(Ext.merge({}, event));
            }
        }

        return this;
    }

}, function() {
    Ext.Object.each(this.priorities, function(priority) {
        this.override(priority, function(message, callerId) {
            if (!callerId) {
                callerId = 1;
            }

            if (typeof callerId == 'number') {
                callerId += 1;
            }

            this.log(message, priority, callerId);
        });
    }, this);
});

})();

Ext.define('Ext.log.filter.Filter', {
    extend: 'Ext.log.Base',

    accept: function(event) {
        return true;
    }
});

Ext.define('Ext.log.filter.Priority', {
    extend: 'Ext.log.filter.Filter',

    config: {
        minPriority: 1
    },

    accept: function(event) {
        return event.priority >= this.getMinPriority();
    }
});

Ext.define('Ext.log.formatter.Formatter', {
    extend: 'Ext.log.Base',

    config: {
        messageFormat: "{message}"
    },

    format: function(event) {
        return this.substitute(this.getMessageFormat(), event);
    },

    substitute: function(template, data) {
        var name, value;

        for (name in data) {
            if (data.hasOwnProperty(name)) {
                value = data[name];

                template = template.replace(new RegExp("\\{" + name + "\\}", "g"), value);
            }
        }

        return template;
    }
});

Ext.define('Ext.log.writer.Writer', {
    extend: 'Ext.log.Base',

    requires: ['Ext.log.formatter.Formatter'],

    config: {
        formatter: null,
        filters: {}
    },

    constructor: function() {
        this.activeFilters = [];

        return this.callParent(arguments);
    },

    updateFilters: function(filters) {
        var activeFilters = this.activeFilters,
            i, filter;

        activeFilters.length = 0;

        for (i in filters) {
            if (filters.hasOwnProperty(i)) {
                filter = filters[i];
                activeFilters.push(filter);
            }
        }
    },

    write: function(event) {
        var filters = this.activeFilters,
            formatter = this.getFormatter(),
            i, ln, filter;

        for (i = 0,ln = filters.length; i < ln; i++) {
            filter = filters[i];

            if (!filters[i].accept(event)) {
                return this;
            }
        }

        if (formatter) {
            event.message = formatter.format(event);
        }

        this.doWrite(event);

        return this;
    },

    
    doWrite: Ext.emptyFn
});


Ext.define('Ext.mixin.Identifiable', {
    statics: {
        uniqueIds: {}
    },

    isIdentifiable: true,

    mixinId: 'identifiable',

    idCleanRegex: /\.|[^\w\-]/g,

    defaultIdPrefix: 'ext-',

    defaultIdSeparator: '-',

    getOptimizedId: function() {
        return this.id;
    },

    getUniqueId: function() {
        var id = this.id,
            prototype, separator, xtype, uniqueIds, prefix;

        if (!id) {
            prototype = this.self.prototype;
            separator = this.defaultIdSeparator;

            uniqueIds = Ext.mixin.Identifiable.uniqueIds;

            if (!prototype.hasOwnProperty('identifiablePrefix')) {
                xtype = this.xtype;

                if (xtype) {
                    prefix = this.defaultIdPrefix + xtype + separator;
                }
                else {
                    prefix = prototype.$className.replace(this.idCleanRegex, separator).toLowerCase() + separator;
                }

                prototype.identifiablePrefix = prefix;
            }

            prefix = this.identifiablePrefix;

            if (!uniqueIds.hasOwnProperty(prefix)) {
                uniqueIds[prefix] = 0;
            }

            id = this.id = prefix + (++uniqueIds[prefix]);
        }

        this.getUniqueId = this.getOptimizedId;

        return id;
    },

    
    getId: function() {
        var id = this.id;

        if (!id) {
            id = this.getUniqueId();
        }

        this.getId = this.getOptimizedId;

        return id;
    }
});


Ext.define('Ext.mixin.Mixin', {
    onClassExtended: function(cls, data) {
        var mixinConfig = data.mixinConfig,
            parentClassMixinConfig,
            beforeHooks, afterHooks;

        if (mixinConfig) {
            parentClassMixinConfig = cls.superclass.mixinConfig;

            if (parentClassMixinConfig) {
                mixinConfig = data.mixinConfig = Ext.merge({}, parentClassMixinConfig, mixinConfig);
            }

            data.mixinId = mixinConfig.id;

            beforeHooks = mixinConfig.beforeHooks,
            afterHooks = mixinConfig.hooks || mixinConfig.afterHooks;

            if (beforeHooks || afterHooks) {
                Ext.Function.interceptBefore(data, 'onClassMixedIn', function(targetClass) {
                    var mixin = this.prototype;

                    if (beforeHooks) {
                        Ext.Object.each(beforeHooks, function(from, to) {
                            targetClass.override(to, function() {
                                mixin[from].apply(this, arguments);

                                return this.callOverridden(arguments);
                            });
                        });
                    }

                    if (afterHooks) {
                        Ext.Object.each(afterHooks, function(from, to) {
                            targetClass.override(to, function() {
                                var ret = this.callOverridden(arguments);

                                mixin[from].apply(this, arguments);

                                return ret;
                            });
                        });
                    }
                });
            }
        }
    }
});


Ext.define('Ext.mixin.Selectable', {
    
    alternateClassName: 'Ext.AbstractStoreSelectionModel',

    extend: 'Ext.mixin.Mixin',

    mixinConfig: {
        id: 'selectable',
        hooks: {
            applyStore : 'applyStore',
            updateStore: 'updateStore'
        }
    },

    config: {
        
        locked: false,

        
        mode: 'SINGLE',

        
        selected: null,

        
        allowDeselect: false,

        
        lastSelected: null,

        
        lastFocused: null,

        
        deselectOnContainerClick: true
    },

    modes: {
        SINGLE: true,
        SIMPLE: true,
        MULTI: true
    },

    constructor: function() {
        this._selected = new Ext.util.MixedCollection();
        this.callParent(arguments);
    },

    applySelected: function(newSelected, selectedCollection) {
        if (newSelected) {
            if (!Ext.isArray(newSelected)) {
                selectedCollection.add(newSelected);
            }
            else {
                selectedCollection.addAll(newSelected);
            }
        }
    },

    applyMode: function(mode) {
        mode = mode ? mode.toUpperCase() : 'SINGLE';
        
        
        return this.modes[mode] ? mode : 'SINGLE';
    },

    applyStore: function(store) {
        var me = this,
            bindEvents = {
                add   : 'onSelectionStoreAdd',
                remove: 'onSelectionStoreRemove',
                update: 'onSelectionStoreUpdate',
                clear : 'onSelectionStoreClear',
                scope: me
            };

        if (store) {
            store = Ext.data.StoreManager.lookup(store);
            if (store && Ext.isObject(store) && store.isStore) {
                store.on(bindEvents);
            }
        }
    },

    updateStore: function(newStore, oldStore) {
        var me = this,
            bindEvents = {
                add   : 'onSelectionStoreAdd',
                remove: 'onSelectionStoreRemove',
                update: 'onSelectionStoreUpdate',
                clear : 'onSelectionStoreClear',
                scope: me
            };

        if (oldStore && Ext.isObject(oldStore) && oldStore.isStore) {
            if (oldStore.autoDestroy) {
                oldStore.destroy();
            }
            else {
                oldStore.un(bindEvents);
            }
        }

        if (newStore) {
            me.refreshSelection();
        }
    },

    selectAll: function(silent) {
        var me = this,
            selections = me.getStore().getRange(),
            ln = selections.length,
            i = 0;
        for (; i < ln; i++) {
            me.select(selections[i], true, silent);
        }
    },

    deselectAll: function() {
        var me = this,
            selections = me.getStore().getRange(),
            ln = selections.length,
            i = 0;
        for (; i < ln; i++) {
            me.deselect(selections[i]);
        }
    },

    
    
    selectWithEvent: function(record) {
        var me = this,
            isSelected = me.isSelected(record);
        switch (me.getMode()) {
            case 'MULTI':
            case 'SIMPLE':
                if (isSelected) {
                    me.deselect(record);
                }
                else {
                    me.select(record, true);
                }
                break;
            case 'SINGLE':
                if (me.getAllowDeselect() && isSelected) {
                    
                    me.deselect(record);
                } else {
                    
                    me.select(record, false);
                }
                break;
        }
    },

    
    selectRange: function(startRecord, endRecord, keepExisting, dir) {
        var me = this,
            store = me.getStore(),
            startRow = store.indexOf(startRecord),
            endRow = store.indexOf(endRecord),
            selectedCount = 0,
            tmp, dontDeselect, i;

        if (me.getLocked()) {
            return;
        }

        
        if (startRow > endRow) {
            tmp = endRow;
            endRow = startRow;
            startRow = tmp;
        }

        for (i = startRow; i <= endRow; i++) {
            if (me.isSelected(store.getAt(i))) {
                selectedCount++;
            }
        }

        if (!dir) {
            dontDeselect = -1;
        }
        else {
            dontDeselect = (dir == 'up') ? startRow : endRow;
        }

        for (i = startRow; i <= endRow; i++) {
            if (selectedCount == (endRow - startRow + 1)) {
                if (i != dontDeselect) {
                    me.deselect(i, true);
                }
            } else {
                me.select(i, true);
            }

        }
    },

    
    select: function(records, keepExisting, suppressEvent) {
        var me = this,
            record;

        if (me.getLocked()) {
            return;
        }

        if (typeof records === "number") {
            records = [me.store.getAt(records)];
        }

        if (me.getMode() == "SINGLE" && records) {
            record = records.length ? records[0] : records;
            me.doSingleSelect(record, suppressEvent);
        } else {
            me.doMultiSelect(records, keepExisting, suppressEvent);
        }
    },

    doSingleSelect: function(record, suppressEvent) {
        var me = this,
            selected = me.getSelected();

        if (me.getLocked()) {
            return;
        }

        
        
        if (me.isSelected(record)) {
            return;
        }

        if (selected.getCount() > 0) {
            me.deselect(me.getLastSelected(), suppressEvent);
        }

        selected.add(record);
        me.setLastSelected(record);
        me.onItemSelect(record, suppressEvent);
        me.setLastFocused(record);
        me.fireSelectionChange(!suppressEvent);
    },

    doMultiSelect: function(records, keepExisting, suppressEvent) {
        if (records === null || this.getLocked()) {
            return;
        }
        records = !Ext.isArray(records) ? [records] : records;

        var me = this,
            selected = me.getSelected(),
            ln = records.length,
            change = false,
            i = 0,
            record;

        if (!keepExisting && selected.getCount() > 0) {
            change = true;
            me.deselect(me.getSelection(), true);
        }
        for (; i < ln; i++) {
            record = records[i];
            if (keepExisting && me.isSelected(record)) {
                continue;
            }
            change = true;
            me.setLastSelected(record);
            selected.add(record);
            if (!suppressEvent) {
                me.setLastFocused(record);
            }

            me.onItemSelect(record, suppressEvent);
        }
        this.fireSelectionChange(change && !suppressEvent);
    },

    
    deselect: function(records, suppressEvent) {
        var me = this,
            selected = me.getSelected(),
            ln = records.length,
            change = false,
            i = 0,
            record;

        if (me.getLocked()) {
            return;
        }

        if (typeof records === "number") {
            records = [me.store.getAt(records)];
        }

        if (!Ext.isArray(records)) {
            records = [records];
            ln = 1;
        }

        for (; i < ln; i++) {
            record = records[i];
            if (selected.remove(record)) {
                if (me.getLastSelected() == record) {
                    me.setLastSelected(selected.last());
                }
                me.onItemDeselect(record, suppressEvent);
                change = true;
            }
        }
        me.fireSelectionChange(change && !suppressEvent);
    },

    
    updateLastFocused: function(newRecord, oldRecord) {
        this.onLastFocusChanged(oldRecord, newRecord);
    },

    fireSelectionChange: function(fireEvent) {
        var me = this;
        if (fireEvent) {
            me.fireAction('beforeselectionchange', [], function() {
                me.fireAction('selectionchange', [me, me.getSelection()], 'doSelectionChange');
            });
        }
    },

    doSelectionChange: Ext.emptyFn,

    
    getSelection: function() {
        return this.getSelected().getRange();
    },

    
    isSelected: function(record) {
        record = Ext.isNumber(record) ? this.getStore().getAt(record) : record;
        return this.getSelected().indexOf(record) !== -1;
    },

    
    hasSelection: function() {
        return this.getSelected().getCount() > 0;
    },

    refreshSelection: function() {
        var me = this,
            newSelection = [],
            oldSelections = me.getSelection(),
            ln = oldSelections.length,
            i = 0,
            selection, change;

        
        
        
        for (; i < ln; i++) {
            selection = oldSelections[i];
            if (me.getStore().indexOf(selection) != -1) {
                newSelection.push(selection);
            }
        }

        
        
        if (me.getSelected().getCount() != newSelection.length) {
            change = true;
        }

        me.clearSelections();

        if (newSelection.length) {
            
            me.select(newSelection, false, true);
        }

        me.fireSelectionChange(change);
    },

    clearSelections: function() {
        
        var me = this;
        me.getSelected().clear();
        me.setLastSelected(null);
        me.setLastFocused(null);
    },

    
    
    onSelectionStoreClear: function() {
        var me = this,
            selected = me.getSelected();
        if (selected.getCount > 0) {
            selected.clear();
            me.setLastSelected(null);
            me.setLastFocused(null);
            me.fireSelectionChange(true);
        }
    },

    
    
    
    onSelectionStoreRemove: function(store, record) {
        var me = this,
            selected = me.getSelected();

        if (me.getLocked()) {
            return;
        }

        if (selected.remove(record)) {
            if (me.getLastSelected() == record) {
                me.setLastSelected(null);
            }
            if (me.getLastFocused() == record) {
                me.setLastFocused(null);
            }
            me.fireSelectionChange(true);
        }
    },

    getCount: function() {
        return this.getSelected().getCount();
    },

    onSelectionStoreAdd: Ext.emptyFn,
    onSelectionStoreUpdate: Ext.emptyFn,
    onItemSelect: Ext.emptyFn,
    onItemDeselect: Ext.emptyFn,
    onLastFocusChanged: Ext.emptyFn,
    onEditorKey: Ext.emptyFn
}, function() {
    

    

    

    

    

     

    Ext.deprecateClassMethod(this, 'isLocked', this.prototype.getLocked, "'isLocked()' is deprecated, please use 'getLocked' instead");
    Ext.deprecateClassMethod(this, 'getSelectionMode', this.prototype.getMode, "'getSelectionMode()' is deprecated, please use 'getMode' instead");
    Ext.deprecateClassMethod(this, 'doDeselect', this.prototype.deselect, "'doDeselect()' is deprecated, please use 'deselect()' instead");
    Ext.deprecateClassMethod(this, 'doSelect', this.prototype.select, "'doSelect()' is deprecated, please use 'select()' instead");
    Ext.deprecateClassMethod(this, 'bind', this.prototype.setStore, "'bind()' is deprecated, please use 'setStore()' instead");
});

Ext.define('Ext.mixin.Traversable', {
    extend: 'Ext.mixin.Mixin',

    mixinConfig: {
        id: 'traversable'
    },

    setParent: function(parent) {
        this.parent = parent;

        return this;
    },

    hasParent: function() {
        return Boolean(this.parent);
    },

    getParent: function() {
        return this.parent;
    },

    getAncestors: function() {
        var ancestors = [],
            parent = this.getParent();

        while (parent) {
            ancestors.push(parent);
            parent = parent.getParent();
        }

        return ancestors;
    },

    getAncestorIds: function() {
        var ancestorIds = [],
            parent = this.getParent();

        while (parent) {
            ancestorIds.push(parent.getId());
            parent = parent.getParent();
        }

        return ancestorIds;
    }
});


Ext.define('Ext.scroll.easing.Easing', {

    config: {
        startTime: 0,
        startValue: 0
    },

    isEnded: false,

    constructor: function(config) {
        this.initConfig(config);

        return this;
    },

    clone: function() {
        var config = this.config,
            cloneConfig = {},
            name;

        for (name in config) {
            if (config.hasOwnProperty(name)) {
                cloneConfig[name] = this[name];
            }
        }

        return new this.self(cloneConfig);
    },

    applyStartTime: function(startTime) {
        if (!startTime) {
            startTime = Ext.Date.now();
        }

        return startTime;
    },

    updateStartTime: function(startTime) {
        this.reset();
    },

    reset: function() {
        this.isEnded = false;
    },

    getValue: Ext.emptyFn
});


Ext.define('Ext.scroll.easing.Linear', {
    extend: 'Ext.scroll.easing.Easing',

    config: {
        duration: 0,
        endValue: 0
    },

    updateStartValue: function(startValue) {
        this.distance = this.getEndValue() - startValue;
    },

    updateEndValue: function(endValue) {
        this.distance = endValue - this.getStartValue();
    },

    getValue: function() {
        var deltaTime = Ext.Date.now() - this.getStartTime(),
            omegaTime = Math.min(1, (deltaTime / this.getDuration()));

        return this.getStartValue() + (omegaTime * this.distance);
    }
});


Ext.define('Ext.scroll.easing.Momentum', {

    extend: 'Ext.scroll.easing.Easing',

    config: {
        acceleration: 30,
        friction: 0,
        startVelocity: 0
    },

    alpha: 0,

    updateFriction: function(friction) {
        var theta = Math.log(1 - (friction / 10));

        this.theta = theta;

        this.alpha = theta / this.getAcceleration();
    },

    updateStartVelocity: function(velocity) {
        this.velocity = velocity * this.getAcceleration();
    },

    updateAcceleration: function(acceleration) {
        this.velocity = this.getStartVelocity() * acceleration;

        this.alpha = this.theta / acceleration;
    },

    getValue: function() {
        return this.getStartValue() - this.velocity * (1 - this.getFrictionFactor()) / this.theta;
    },

    getFrictionFactor: function() {
        var deltaTime = Ext.Date.now() - this.getStartTime();

        return Math.exp(deltaTime * this.alpha);
    },

    getVelocity: function() {
        return this.getFrictionFactor() * this.velocity;
    }
});


Ext.define('Ext.util.Format', {
    singleton: true,
    defaultDateFormat: 'm/d/Y',
    escapeRe: /('|\\)/g,
    trimRe: /^[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u2028\u2029\u202f\u205f\u3000]+|[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u2028\u2029\u202f\u205f\u3000]+$/g,
    formatRe: /\{(\d+)\}/g,
    escapeRegexRe: /([-.*+?^${}()|[\]\/\\])/g,

    /**
     * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
     * @param {String} value The string to truncate
     * @param {Number} length The maximum length to allow before truncating
     * @param {Boolean} word True to try to find a common word break
     * @return {String} The converted text
     */
    ellipsis: function(value, len, word) {
        if (value && value.length > len) {
            if (word) {
                var vs = value.substr(0, len - 2),
                index = Math.max(vs.lastIndexOf(' '), vs.lastIndexOf('.'), vs.lastIndexOf('!'), vs.lastIndexOf('?'));
                if (index != -1 && index >= (len - 15)) {
                    return vs.substr(0, index) + "...";
                }
            }
            return value.substr(0, len - 3) + "...";
        }
        return value;
    },

    /**
     * Escapes the passed string for use in a regular expression
     * @param {String} str
     * @return {String}
     */
    escapeRegex: function(s) {
        return s.replace(Ext.util.Format.escapeRegexRe, "\\$1");
    },

    /**
     * Escapes the passed string for ' and \
     * @param {String} string The string to escape
     * @return {String} The escaped string
     */
    escape: function(string) {
        return string.replace(Ext.util.Format.escapeRe, "\\$1");
    },

    
    toggle: function(string, value, other) {
        return string == value ? other : value;
    },

    
    trim: function(string) {
        return string.replace(Ext.util.Format.trimRe, "");
    },

    
    leftPad: function (val, size, ch) {
        var result = String(val);
        ch = ch || " ";
        while (result.length < size) {
            result = ch + result;
        }
        return result;
    },

    
    format: function (format) {
        var args = Ext.toArray(arguments, 1);
        return format.replace(Ext.util.Format.formatRe, function(m, i) {
            return args[i];
        });
    },

    
    htmlEncode: function(value) {
        return ! value ? value: String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
    },

    
    htmlDecode: function(value) {
        return ! value ? value: String(value).replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"').replace(/&amp;/g, "&");
    },

    
    date: function(v, format) {
        if (!v) {
            return "";
        }
        if (!Ext.isDate(v)) {
            v = new Date(Date.parse(v));
        }
        return v.dateFormat(format || Ext.util.Format.defaultDateFormat);
    }
});


Ext.define('Ext.util.JSONP', {
    singleton: true,

    
    queue: [],

    
    current: null,

    
    request: function(o) {
        o = o || {};
        if (!o.url) {
            return;
        }

        var me = this;
        o.params = o.params || {};
        if (o.callbackKey) {
            o.params[o.callbackKey] = 'Ext.util.JSONP.callback';
        }
        var params = Ext.urlEncode(o.params);

        var script = document.createElement('script');
        script.type = 'text/javascript';

        this.queue.push({
            url: o.url,
            script: script,
            callback: o.callback || function(){},
            scope: o.scope || window,
            params: params || null
        });

        if (!this.current) {
            this.next();
        }
    },

    
    next: function() {
        this.current = null;
        if (this.queue.length) {
            this.current = this.queue.shift();
            this.current.script.src = this.current.url + (this.current.params ? ('?' + this.current.params) : '');
            document.getElementsByTagName('head')[0].appendChild(this.current.script);
        }
    },

    
    callback: function(json) {
        this.current.callback.call(this.current.scope, json);
        document.getElementsByTagName('head')[0].removeChild(this.current.script);
        this.next();
    }
});

Ext.define('Ext.util.OffsetConstraint', {
    config: {
        from: null,

        to: null
    },

    constructor: function(from, to) {
        var fromBox = this.getBoundingBox(from),
            toBox = this.getBoundingBox(to);
    },

    applyFrom: function() {

    },

    getBoundingBox: function(dom) {
        return dom.getBoundingClientRect();
    }
});


Ext.Anim = Ext.extend(Object, {
    isAnim: true,

    
    disableAnimations: false,


    defaultConfig: {
        
        from: {},

        
        to: {},

        
        duration: 250,

        
        delay: 0,

        
        easing: 'ease-in-out',

        
        autoClear: true,

        
        out: true,

        
        direction: null,

        
        reverse: false
    },

    

    

    opposites: {
        'left': 'right',
        'right': 'left',
        'up': 'down',
        'down': 'up'
    },

    constructor: function(config) {
        config = Ext.apply({}, config || {}, this.defaultConfig);
        this.config = config;

        Ext.Anim.superclass.constructor.call(this);

        this.running = [];
    },

    initConfig: function(el, runConfig) {
        var me = this,
            runtime = {},
            config = Ext.apply({}, runConfig || {}, me.config);

        config.el = el = Ext.get(el);

        if (config.reverse && me.opposites[config.direction]) {
            config.direction = me.opposites[config.direction];
        }

        if (me.config.before) {
            me.config.before.call(config, el, config);
        }

        if (runConfig.before) {
            runConfig.before.call(config.scope || config, el, config);
        }

        return config;
    },

    run: function(el, config) {
        el = Ext.get(el);
        config = config || {};


        var me = this,
            style = el.dom.style,
            property,
            after = config.after;

        if (me.running[el.id]) {
            me.onTransitionEnd(null, el, {
                config: config,
                after: after
            });
        }

        config = this.initConfig(el, config);

        if (this.disableAnimations) {
            for (property in config.to) {
                if (!config.to.hasOwnProperty(property)) {
                    continue;
                }
                style[property] = config.to[property];
            }
            this.onTransitionEnd(null, el, {
                config: config,
                after: after
            });
            return me;
        }

        el.un('webkitTransitionEnd', me.onTransitionEnd, me);

        style.webkitTransitionDuration = '0ms';
        for (property in config.from) {
            if (!config.from.hasOwnProperty(property)) {
                continue;
            }
            style[property] = config.from[property];
        }

        setTimeout(function() {
            
            if (!el.dom) {
                return;
            }

            
            if (config.is3d === true) {
                el.parent().setStyle({
                    
                    '-webkit-perspective': '1200',
                    '-webkit-transform-style': 'preserve-3d'
                });
            }

            style.webkitTransitionDuration = config.duration + 'ms';
            style.webkitTransitionProperty = 'all';
            style.webkitTransitionTimingFunction = config.easing;

            
            el.on('webkitTransitionEnd', me.onTransitionEnd, me, {
                single: true,
                config: config,
                after: after
            });

            for (property in config.to) {
                if (!config.to.hasOwnProperty(property)) {
                    continue;
                }
                style[property] = config.to[property];
            }
        }, config.delay || 5);

        me.running[el.id] = config;
        return me;
    },

    onTransitionEnd: function(ev, el, o) {
        el = Ext.get(el);

        if (this.running[el.id] === undefined) {
            return;
        }

        var style = el.dom.style,
            config = o.config,
            property,
            me = this;

        if (config.autoClear) {
            for (property in config.to) {
                if (!config.to.hasOwnProperty(property)) {
                    continue;
                }
                style[property] = '';
            }
        }

        style.webkitTransitionDuration = null;
        style.webkitTransitionProperty = null;
        style.webkitTransitionTimingFunction = null;

        if (config.is3d) {
            el.parent().setStyle({
                '-webkit-perspective': '',
                '-webkit-transform-style': ''
            });
        }

        if (me.config.after) {
            me.config.after.call(config, el, config);
        }

        if (o.after) {
            o.after.call(config.scope || me, el, config);
        }

        delete me.running[el.id];
    }
});

Ext.Anim.seed = 1000;


Ext.Anim.run = function(el, anim, config) {
    if (el.isComponent) {
        el = el.el;
    }

    config = config || {};

    if (anim.isAnim) {
        anim.run(el, config);
    }
    else {
        if (Ext.isObject(anim)) {
            if (config.before && anim.before) {
                config.before = Ext.createInterceptor(config.before, anim.before, anim.scope);
            }
            if (config.after && anim.after) {
                config.after = Ext.createInterceptor(config.after, anim.after, anim.scope);
            }
            config = Ext.apply({}, config, anim);
            anim = anim.type;
        }

        if (!Ext.anims[anim]) {
            throw anim + ' is not a valid animation type.';
        }
        else {
            
            if (el && el.dom) {
                Ext.anims[anim].run(el, config);
            }

        }
    }
};


Ext.anims = {
    
    fade: new Ext.Anim({
        before: function(el) {
            var fromOpacity = 1,
                toOpacity = 1,
                curZ = el.getStyle('z-index') == 'auto' ? 0 : el.getStyle('z-index'),
                zIndex = curZ;

            if (this.out) {
                toOpacity = 0;
            } else {
                zIndex = curZ + 1;
                fromOpacity = 0;
            }

            this.from = {
                'opacity': fromOpacity,
                'z-index': zIndex
            };
            this.to = {
                'opacity': toOpacity,
                'z-index': zIndex
            };
        }
    }),

    
    slide: new Ext.Anim({
        direction: 'left',
        cover: false,
        reveal: false,

        before: function(el) {
            var curZ = el.getStyle('z-index') == 'auto' ? 0 : el.getStyle('z-index'),
                zIndex = curZ + 1,
                toX = 0,
                toY = 0,
                fromX = 0,
                fromY = 0,
                elH = el.getHeight(),
                elW = el.getWidth();

            if (this.direction == 'left' || this.direction == 'right') {
                if (this.out == true) {
                    toX = -elW;
                }
                else {
                    fromX = elW;
                }
            }
            else if (this.direction == 'up' || this.direction == 'down') {
                if (this.out == true) {
                    toY = -elH;
                }
                else {
                    fromY = elH;
                }
            }

            if (this.direction == 'right' || this.direction == 'down') {
                toY *= -1;
                toX *= -1;
                fromY *= -1;
                fromX *= -1;
            }

            if (this.cover && this.out) {
                toX = 0;
                toY = 0;
                zIndex = curZ;
            }
            else if (this.reveal && !this.out) {
                fromX = 0;
                fromY = 0;
                zIndex = curZ;
            }

            this.from = {
                '-webkit-transform': 'translate3d(' + fromX + 'px, ' + fromY + 'px, 0)',
                'z-index': zIndex,
                'opacity': 0.99
            };
            this.to = {
                '-webkit-transform': 'translate3d(' + toX + 'px, ' + toY + 'px, 0)',
                'z-index': zIndex,
                'opacity': 1
            };
        }
    }),

    
    pop: new Ext.Anim({
        scaleOnExit: true,
        before: function(el) {
            var fromScale = 1,
                toScale = 1,
                fromOpacity = 1,
                toOpacity = 1,
                curZ = el.getStyle('z-index') == 'auto' ? 0 : el.getStyle('z-index'),
                fromZ = curZ,
                toZ = curZ;

            if (!this.out) {
                fromScale = 0.01;
                fromZ = curZ + 1;
                toZ = curZ + 1;
                fromOpacity = 0;
            }
            else {
                if (this.scaleOnExit) {
                    toScale = 0.01;
                    toOpacity = 0;
                } else {
                    toOpacity = 0.8;
                }
            }

            this.from = {
                '-webkit-transform': 'scale(' + fromScale + ')',
                '-webkit-transform-origin': '50% 50%',
                'opacity': fromOpacity,
                'z-index': fromZ
            };

            this.to = {
                '-webkit-transform': 'scale(' + toScale + ')',
                '-webkit-transform-origin': '50% 50%',
                'opacity': toOpacity,
                'z-index': toZ
            };
        }
    })
};

Ext.define('Ext.util.Timeline', {

    requires: ['Ext.Anim'],

    constructor: function(anims) {
        this.callParent();

        if (anims) {
            this.add(anims);
        }
    },

    play: function() {
        this.playing = true;
        this.queue = this.anims.slice();
        this.next();
    },

    next: function() {
        if (this.queue.length) {
            var anim = this.queue.shift();
            if (Ext.isObject(anim)) {
                anim = Ext.create('Ext.Anim', anim);
                anim.run(anim.target, {
                    after: function() {
                        this.next();
                    },
                    scope: this
                });
            }
            else if (Ext.isArray(anim)) {
                var anims = anim,
                    ln = anims.length,
                    i, longest = anims[0];

                for (i = 0; i < ln; i++) {
                    anim = anims[i];
                    anim = Ext.create('Ext.Anim', anim);
                    if ((anim.duration + anim.delay) >= (longest.duration + anim.delay)) {
                        longest = anim;
                    }
                    anims[i] = anim;
                }

                for (i = 0; i < ln; i++) {
                    anim = anims[i];
                    if (longest === anim) {
                        anim.after = function() {
                            this.next();
                        };
                        anim.scope = this;
                    }
                    anim.run(anim.target);
                }
            }
        }
    },

    pause: function() {
        
    },

    reset: function() {
        
    },

    clear: function() {
        if (this.playing) {
            
        }
        this.anims = [];
    },

    stop: function() {
        if (this.playing) {
            this.pause();
            
        }
    },

    add: function(anims) {
        this.anims = this.anims || [];
        if (Ext.isArray(anims)) {
            this.anims = this.anims.concat(anims);
        }
        else {
            this.anims.push(anims);
        }
    }
});

Ext.define('Ext.Template', {

    

    requires: ['Ext.dom.Helper', 'Ext.util.Format'],

    inheritableStatics: {
        
        from: function(el, config) {
            el = Ext.getDom(el);
            return new this(el.value || el.innerHTML, config || '');
        }
    },

    

    
    constructor: function(html) {
        var me = this,
            args = arguments,
            buffer = [],
            i = 0,
            length = args.length,
            value;

        me.initialConfig = {};

        if (length > 1) {
            for (; i < length; i++) {
                value = args[i];
                if (typeof value == 'object') {
                    Ext.apply(me.initialConfig, value);
                    Ext.apply(me, value);
                } else {
                    buffer.push(value);
                }
            }
            html = buffer.join('');
        } else {
            if (Ext.isArray(html)) {
                buffer.push(html.join(''));
            } else {
                buffer.push(html);
            }
        }

        
        me.html = buffer.join('');

        if (me.compiled) {
            me.compile();
        }
    },

    isTemplate: true,

    

    
    disableFormats: false,

    re: /\{([\w\-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,

    
    apply: function(values) {
        var me = this,
            useFormat = me.disableFormats !== true,
            fm = Ext.util.Format,
            tpl = me,
            ret;

        if (me.compiled) {
            return me.compiled(values).join('');
        }

        function fn(m, name, format, args) {
            if (format && useFormat) {
                if (args) {
                    args = [values[name]].concat(Ext.functionFactory('return ['+ args +'];')());
                } else {
                    args = [values[name]];
                }
                if (format.substr(0, 5) == "this.") {
                    return tpl[format.substr(5)].apply(tpl, args);
                }
                else {
                    return fm[format].apply(fm, args);
                }
            }
            else {
                return values[name] !== undefined ? values[name] : "";
            }
        }

        ret = me.html.replace(me.re, fn);
        return ret;
    },

    
    applyOut: function(values, out) {
        var me = this;

        if (me.compiled) {
            out.push.apply(out, me.compiled(values));
        } else {
            out.push(me.apply(values));
        }

        return out;
    },

    
    applyTemplate: function () {
        return this.apply.apply(this, arguments);
    },

    
    set: function(html, compile) {
        var me = this;
        me.html = html;
        me.compiled = null;
        return compile ? me.compile() : me;
    },

    compileARe: /\\/g,
    compileBRe: /(\r\n|\n)/g,
    compileCRe: /'/g,

    /**
     * Compiles the template into an internal function, eliminating the RegEx overhead.
     * @return {Ext.Template} this
     */
    compile: function() {
        var me = this,
            fm = Ext.util.Format,
            useFormat = me.disableFormats !== true,
            body, bodyReturn;

        function fn(m, name, format, args) {
            if (format && useFormat) {
                args = args ? ',' + args: "";
                if (format.substr(0, 5) != "this.") {
                    format = "fm." + format + '(';
                }
                else {
                    format = 'this.' + format.substr(5) + '(';
                }
            }
            else {
                args = '';
                format = "(values['" + name + "'] == undefined ? '' : ";
            }
            return "'," + format + "values['" + name + "']" + args + ") ,'";
        }

        bodyReturn = me.html.replace(me.compileARe, '\\\\').replace(me.compileBRe, '\\n').replace(me.compileCRe, "\\'").replace(me.re, fn);
        body = "this.compiled = function(values){ return ['" + bodyReturn + "'];};";
        eval(body);
        return me;
    },

    /**
     * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
     *
     * @param {String/HTMLElement/Ext.Element} el The context element
     * @param {Object/Array} values The template values. See {@link #applyTemplate} for details.
     * @param {Boolean} returnElement (optional) true to return a Ext.Element.
     * @return {HTMLElement/Ext.Element} The new node or Element
     */
    insertFirst: function(el, values, returnElement) {
        return this.doInsert('afterBegin', el, values, returnElement);
    },

    /**
     * Applies the supplied values to the template and inserts the new node(s) before el.
     *
     * @param {String/HTMLElement/Ext.Element} el The context element
     * @param {Object/Array} values The template values. See {@link #applyTemplate} for details.
     * @param {Boolean} returnElement (optional) true to return a Ext.Element.
     * @return {HTMLElement/Ext.Element} The new node or Element
     */
    insertBefore: function(el, values, returnElement) {
        return this.doInsert('beforeBegin', el, values, returnElement);
    },

    /**
     * Applies the supplied values to the template and inserts the new node(s) after el.
     *
     * @param {String/HTMLElement/Ext.Element} el The context element
     * @param {Object/Array} values The template values. See {@link #applyTemplate} for details.
     * @param {Boolean} returnElement (optional) true to return a Ext.Element.
     * @return {HTMLElement/Ext.Element} The new node or Element
     */
    insertAfter: function(el, values, returnElement) {
        return this.doInsert('afterEnd', el, values, returnElement);
    },

    /**
     * Applies the supplied `values` to the template and appends the new node(s) to the specified `el`.
     *
     * For example usage see {@link Ext.Template Ext.Template class docs}.
     *
     * @param {String/HTMLElement/Ext.Element} el The context element
     * @param {Object/Array} values The template values. See {@link #applyTemplate} for details.
     * @param {Boolean} returnElement (optional) true to return an Ext.Element.
     * @return {HTMLElement/Ext.Element} The new node or Element
     */
    append: function(el, values, returnElement) {
        return this.doInsert('beforeEnd', el, values, returnElement);
    },

    doInsert: function(where, el, values, returnEl) {
        el = Ext.getDom(el);
        var newNode = Ext.DomHelper.insertHtml(where, el, this.apply(values));
        return returnEl ? Ext.get(newNode, true) : newNode;
    },

    /**
     * Applies the supplied values to the template and overwrites the content of el with the new node(s).
     *
     * @param {String/HTMLElement/Ext.Element} el The context element
     * @param {Object/Array} values The template values. See {@link #applyTemplate} for details.
     * @param {Boolean} returnElement (optional) true to return a Ext.Element.
     * @return {HTMLElement/Ext.Element} The new node or Element
     */
    overwrite: function(el, values, returnElement) {
        el = Ext.getDom(el);
        el.innerHTML = this.apply(values);
        return returnElement ? Ext.get(el.firstChild, true) : el.firstChild;
    }
});

/**
 * This class compiles the XTemplate syntax into a function object. The function is used
 * like so:
 * 
 *      function (out, values, parent, xindex, xcount) {
 *          // out is the output array to store results
 *          // values, parent, xindex and xcount have their historical meaning
 *      }
 *
 * @markdown
 * @private
 */
Ext.define('Ext.XTemplateCompiler', {
    extend: 'Ext.XTemplateParser',

    // Chrome really likes "new Function" to realize the code block (as in it is
    // 2x-3x faster to call it than using eval), but Firefox chokes on it badly.
    // IE and Opera are also fine with the "new Function" technique.
    useEval: Ext.isGecko,

    useFormat: true,

    compile: function (tpl) {
        var me = this,
            code = me.generate(tpl),
            fm = Ext.util.Format;

        return me.useEval ? me.evalTpl(fm, code) : (new Function('fm', code))(fm);
    },

    generate: function (tpl) {
        var me = this;

        me.body = [
            'var c0=values, p0=parent, n0=xcount, i0=xindex;\n'
        ];
        me.funcs = [];
        me.switches = [];

        me.parse(tpl);

        me.funcs.push(
            (me.useEval ? 'var $=' : 'return') + ' function (' + me.fnArgs + ') {',
                me.body.join(''),
            '}'
        );

        var code = me.funcs.join('\n');

        return code;
    },

    //-----------------------------------
    // XTemplateParser callouts

    doText: function (text) {
        this.body.push('out.push(\'', text.replace(this.aposRe, "\\'"), '\')\n');
    },

    doExpr: function (expr) {
        this.body.push('out.push(String(', expr, '))\n');
    },

    doTag: function (tag) {
        this.doExpr(this.parseTag(tag));
    },

    doElse: function () {
        this.body.push('} else {\n');
    },

    doEval: function (text) {
        this.body.push(text, '\n');
    },

    doIf: function (action, actions) {
        var me = this,
            s = me.addFn(action);

        me.body.push('if (', s, me.callFn, ') {\n');
        if (actions.exec) {
            me.doExec(actions.exec);
        }
    },

    doElseIf: function (action, actions) {
        var me = this,
            s = me.addFn(action);

        me.body.push('} else if (', s, me.callFn, ') {\n');
        if (actions.exec) {
            me.doExec(actions.exec);
        }
    },

    doSwitch: function (action) {
        var me = this,
            s = me.addFn(action);

        me.body.push('switch (', s, me.callFn, ') {\n');
        me.switches.push(0);
    },

    doCase: function (action) {
        var me = this,
            cases = Ext.isArray(action) ? action : [action],
            n = me.switches.length - 1,
            match, i;

        if (me.switches[n]) {
            me.body.push('break;\n');
        } else {
            me.switches[n]++;
        }

        for (i = 0, n = cases.length; i < n; ++i) {
            match = me.intRe.exec(cases[i]);
            cases[i] = match ? match[1] : ("'" + cases[i].replace(me.aposRe,"\\'") + "'");
        }

        me.body.push('case ', cases.join(': case '), ':\n');
    },

    doDefault: function () {
        var me = this,
            n = me.switches.length - 1;

        if (me.switches[n]) {
            me.body.push('break;\n');
        } else {
            me.switches[n]++;
        }

        me.body.push('default:\n');
    },

    doEnd: function (type, actions) {
        var me = this,
            L = me.level-1;

        if (type == 'for') {
            
            if (actions.exec) {
                me.doExec(actions.exec);
            }

            me.body.push('}\n');
            me.body.push('parent=p',L,';values=r',L+1,';xcount=n',L,';xindex=i',L,'\n');
        } else if (type == 'if' || type == 'switch') {
            me.body.push('}\n');
        }
    },

    doFor: function (action, actions) {
        var me = this,
            s = me.addFn(action),
            L = me.level,
            up = L-1;

        

        me.body.push('var c',L,'=',s,me.callFn,', a',L,'=Ext.isArray(c',L,'),p',L,'=(parent=c',up,'),r',L,'=values\n',
            'for (var i',L,'=0,n',L,'=a',L,'?c',L,'.length:(c',L,'?1:0), xcount=n',L,';i',L,'<n'+L+';++i',L,'){\n',
            'values=a',L,'?c',L,'[i',L,']:c',L,'\n',
            'xindex=i',L,'+1\n');
    },

    doExec: function (action, actions) {
        var me = this,
            name = 'f' + me.funcs.length;

        me.funcs.push('function ' + name + '(' + me.fnArgs + ') {',
                            ' try { with(values) {',
                            '  ' + action,
                            ' }} catch(e) {}',
                      '}');

        me.body.push(name + me.callFn + '\n');
    },

    
    

    addFn: function (body) {
        var me = this,
            name = 'f' + me.funcs.length;

        if (body === '.') {
            me.funcs.push('function ' + name + '(' + me.fnArgs + ') {',
                            ' return values',
                       '}');
        } else if (body === '..') {
            me.funcs.push('function ' + name + '(' + me.fnArgs + ') {',
                            ' return parent',
                       '}');
        } else {
            me.funcs.push('function ' + name + '(' + me.fnArgs + ') {',
                            ' try { with(values) {',
                            '  return(' + body + ')',
                            ' }} catch(e) {}',
                       '}');
        }

        return name;
    },

    parseTag: function (tag) {
        var m = this.tagRe.exec(tag),
            name = m[1],
            format = m[2],
            args = m[3],
            math = m[4],
            v;

        
        if (name == '.') {
            
            v = 'Ext.Array.indexOf(["string", "number", "boolean"], typeof values) > -1 || Ext.isDate(values) ? values : ""';
        }
        
        else if (name == '#') {
            v = 'xindex';
        }
        else if (name.substr(0, 7) == "parent.") {
            v = name;
        }
        
        else if (name.indexOf('.') != -1) {
            v = "values." + name;
        }
        
        else {
            v = "values['" + name + "']";
        }

        if (math) {
            v = '(' + v + math + ')';
        }

        if (format && this.useFormat) {
            args = args ? ',' + args : "";
            if (format.substr(0, 5) != "this.") {
                format = "fm." + format + '(';
            } else {
                format = 'this.' + format.substr(5) + '(';
            }
        } else {
            args = '';
            format = "(" + v + " === undefined ? '' : ";
        }

        return format + v + args + ')';
    },

    
    evalTpl: function (fm) { 

        
        
        
        
        var $;
        eval(arguments[1]);
        return $;
    },

    aposRe: /[']/g,
    intRe:  /^\s*(\d+)\s*$/,
    tagRe:  /([\w-\.\#]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?(\s?[\+\-\*\/]\s?[\d\.\+\-\*\/\(\)]+)?/

}, function () {
    var proto = this.prototype;

    proto.fnArgs = 'out,values,parent,xindex,xcount';
    proto.callFn = '.call(this,' + proto.fnArgs + ')';
});

/**
 * A template class that supports advanced functionality like:
 *
 * - Autofilling arrays using templates and sub-templates
 * - Conditional processing with basic comparison operators
 * - Basic math function support
 * - Execute arbitrary inline code with special built-in template variables
 * - Custom member functions
 * - Many special tags and built-in operators that aren't defined as part of the API, but are supported in the templates that can be created
 *
 * XTemplate provides the templating mechanism built into {@link Ext.dataview.DataView}.
 *
 * The {@link Ext.Template} describes the acceptable parameters to pass to the constructor. The following examples
 * demonstrate all of the supported features.
 *
 * # Sample Data
 *
 * This is the data object used for reference in each code example:
 *
 *     var data = {
 *         name: 'Don Griffin',
 *         title: 'Senior Technomage',
 *         company: 'Sencha Inc.',
 *         drinks: ['Coffee', 'Water', 'More Coffee'],
 *         kids: [
 *             { name: 'Aubrey',  age: 17 },
 *             { name: 'Joshua',  age: 13 },
 *             { name: 'Cale',    age: 10 },
 *             { name: 'Nikol',   age: 5 },
 *             { name: 'Solomon', age: 0 }
 *         ]
 *     };
 *
 * # Auto filling of arrays
 *
 * The **tpl** tag and the **for** operator are used to process the provided data object:
 *
 * - If the value specified in for is an array, it will auto-fill, repeating the template block inside the tpl
 *   tag for each item in the array.
 * - If for="." is specified, the data object provided is examined.
 * - While processing an array, the special variable {#} will provide the current array index + 1 (starts at 1, not 0).
 *
 * Examples:
 *
 *     <tpl for=".">...</tpl>       
 *     <tpl for="foo">...</tpl>     
 *     <tpl for="foo.bar">...</tpl> 
 *
 * Using the sample data above:
 *
 *     var tpl = new Ext.XTemplate(
 *         '<p>Kids: ',
 *         '<tpl for=".">',       
 *             '<p>{#}. {name}</p>',  
 *         '</tpl></p>'
 *     );
 *     tpl.overwrite(panel.body, data.kids); 
 *
 * An example illustrating how the **for** property can be leveraged to access specified members of the provided data
 * object to populate the template:
 *
 *     var tpl = new Ext.XTemplate(
 *         '<p>Name: {name}</p>',
 *         '<p>Title: {title}</p>',
 *         '<p>Company: {company}</p>',
 *         '<p>Kids: ',
 *         '<tpl for="kids">',     
 *             '<p>{name}</p>',
 *         '</tpl></p>'
 *     );
 *     tpl.overwrite(panel.body, data);  
 *
 * Flat arrays that contain values (and not objects) can be auto-rendered using the special **`{.}`** variable inside a
 * loop. This variable will represent the value of the array at the current index:
 *
 *     var tpl = new Ext.XTemplate(
 *         '<p>{name}\'s favorite beverages:</p>',
 *         '<tpl for="drinks">',
 *             '<div> - {.}</div>',
 *         '</tpl>'
 *     );
 *     tpl.overwrite(panel.body, data);
 *
 * When processing a sub-template, for example while looping through a child array, you can access the parent object's
 * members via the **parent** object:
 *
 *     var tpl = new Ext.XTemplate(
 *         '<p>Name: {name}</p>',
 *         '<p>Kids: ',
 *         '<tpl for="kids">',
 *             '<tpl if="age &gt; 1">',
 *                 '<p>{name}</p>',
 *                 '<p>Dad: {parent.name}</p>',
 *             '</tpl>',
 *         '</tpl></p>'
 *     );
 *     tpl.overwrite(panel.body, data);
 *
 * # Conditional processing with basic comparison operators
 *
 * The **tpl** tag and the **if** operator are used to provide conditional checks for deciding whether or not to render
 * specific parts of the template.
 *
 * Using the sample data above:
 *
 *     var tpl = new Ext.XTemplate(
 *         '<p>Name: {name}</p>',
 *         '<p>Kids: ',
 *         '<tpl for="kids">',
 *             '<tpl if="age &gt; 1">',
 *                 '<p>{name}</p>',
 *             '</tpl>',
 *         '</tpl></p>'
 *     );
 *     tpl.overwrite(panel.body, data);
 *
 * More advanced conditionals are also supported:
 *
 *     var tpl = new Ext.XTemplate(
 *         '<p>Name: {name}</p>',
 *         '<p>Kids: ',
 *         '<tpl for="kids">',
 *             '<p>{name} is a ',
 *             '<tpl if="age &gt;= 13">',
 *                 '<p>teenager</p>',
 *             '<tpl elseif="age &gt;= 2">',
 *                 '<p>kid</p>',
 *             '<tpl else">',
 *                 '<p>baby</p>',
 *             '</tpl>',
 *         '</tpl></p>'
 *     );
 *
 *     var tpl = new Ext.XTemplate(
 *         '<p>Name: {name}</p>',
 *         '<p>Kids: ',
 *         '<tpl for="kids">',
 *             '<p>{name} is a ',
 *             '<tpl switch="name">',
 *                 '<tpl case="Aubrey" case="Nikol">',
 *                     '<p>girl</p>',
 *                 '<tpl default">',
 *                     '<p>boy</p>',
 *             '</tpl>',
 *         '</tpl></p>'
 *     );
 *
 * A `break` is implied between each case and default, however, multiple cases can be listed
 * in a single &lt;tpl&gt; tag.
 *
 * # Using double quotes
 *
 * Examples:
 *
 *     var tpl = new Ext.XTemplate(
 *         "<tpl if='age > 1 && age < 10'>Child</tpl>",
 *         "<tpl if='age >= 10 && age < 18'>Teenager</tpl>",
 *         "<tpl if='this.isGirl(name)'>...</tpl>",
 *         '<tpl if="id == \'download\'">...</tpl>',
 *         "<tpl if='needsIcon'><img src='{icon}' class='{iconCls}'/></tpl>",
 *         "<tpl if='name == \"Don\"'>Hello</tpl>"
 *     );
 *
 * # Basic math support
 *
 * The following basic math operators may be applied directly on numeric data values:
 *
 *     + - * /
 *
 * For example:
 *
 *     var tpl = new Ext.XTemplate(
 *         '<p>Name: {name}</p>',
 *         '<p>Kids: ',
 *         '<tpl for="kids">',
 *             '<tpl if="age &gt; 1">',  
 *                 '<p>{#}: {name}</p>',  
 *                 '<p>In 5 Years: {age+5}</p>',  
 *                 '<p>Dad: {parent.name}</p>',
 *             '</tpl>',
 *         '</tpl></p>'
 *     );
 *     tpl.overwrite(panel.body, data);
 *
 * # Execute arbitrary inline code with special built-in template variables
 *
 * Anything between `{[ ... ]}` is considered code to be executed in the scope of the template.
 * The expression is evaluated and the result is included in the generated result. There are
 * some special variables available in that code:
 *
 * - **out**: The output array into which the template is being appended (using `push` to later
 *   `join`).
 * - **values**: The values in the current scope. If you are using scope changing sub-templates,
 *   you can change what values is.
 * - **parent**: The scope (values) of the ancestor template.
 * - **xindex**: If you are in a looping template, the index of the loop you are in (1-based).
 * - **xcount**: If you are in a looping template, the total length of the array you are looping.
 *
 * This example demonstrates basic row striping using an inline code block and the xindex variable:
 *
 *     var tpl = new Ext.XTemplate(
 *         '<p>Name: {name}</p>',
 *         '<p>Company: {[values.company.toUpperCase() + ", " + values.title]}</p>',
 *         '<p>Kids: ',
 *         '<tpl for="kids">',
 *             '<div class="{[xindex % 2 === 0 ? "even" : "odd"]}">',
 *             '{name}',
 *             '</div>',
 *         '</tpl></p>'
 *      );
 *
 * Any code contained in "verbatim" blocks (using "{% ... %}") will be inserted directly in
 * the generated code for the template. These blocks are not included in the output. This
 * can be used for simple things like break/continue in a loop, or control structures or
 * method calls (when they don't produce output). The `this` references the template instance.
 *
 *     var tpl = new Ext.XTemplate(
 *         '<p>Name: {name}</p>',
 *         '<p>Company: {[values.company.toUpperCase() + ", " + values.title]}</p>',
 *         '<p>Kids: ',
 *         '<tpl for="kids">',
 *             '{% if (xindex % 2 === 0) continue; %}',
 *             '{name}',
 *             '{% if (xindex > 100) break; %}',
 *             '</div>',
 *         '</tpl></p>'
 *      );
 *
 * # Template member functions
 *
 * One or more member functions can be specified in a configuration object passed into the XTemplate constructor for
 * more complex processing:
 *
 *     var tpl = new Ext.XTemplate(
 *         '<p>Name: {name}</p>',
 *         '<p>Kids: ',
 *         '<tpl for="kids">',
 *             '<tpl if="this.isGirl(name)">',
 *                 '<p>Girl: {name} - {age}</p>',
 *             '<tpl else>',
 *                 '<p>Boy: {name} - {age}</p>',
 *             '</tpl>',
 *             '<tpl if="this.isBaby(age)">',
 *                 '<p>{name} is a baby!</p>',
 *             '</tpl>',
 *         '</tpl></p>',
 *         {
 *             // XTemplate configuration:
 *             disableFormats: true,
 *             // member functions:
 *             isGirl: function(name){
 *                return name == 'Sara Grace';
 *             },
 *             isBaby: function(age){
 *                return age < 1;
 *             }
 *         }
 *     );
 *     tpl.overwrite(panel.body, data);
 */
Ext.define('Ext.XTemplate', {
    extend: 'Ext.Template',

    requires: 'Ext.XTemplateCompiler',

    /**
     * @cfg {Boolean} compiled
     * Only applies to {@link Ext.Template}, XTemplates are compiled automatically on the
     * first call to {@link #apply} or {@link #applyOut}.
     */

    apply: function(values) {
        return this.applyOut(values, []).join('');
    },

    applyOut: function(values, out) {
        var me = this,
            compiler;

        if (!me.fn) {
            compiler = new Ext.XTemplateCompiler({
                useFormat: me.disableFormats !== true
            });

            me.fn = compiler.compile(me.html);
        }

        try {
            me.fn.call(me, out, values, {}, 1, 1);
        } catch (e) {
            Ext.log('Error: ' + e.message);
        }

        return out;
    },

    /**
     * Does nothing. XTemplates are compiled automatically, so this function simply returns this.
     * @return {Ext.XTemplate} this
     */
    compile: function() {
        return this;
    }
});

/**
 * @extends Object
 * @author Ed Spencer
 *
 * Fields are used to define what a Model is. They aren't instantiated directly - instead, when we create a class that
 * extends {@link Ext.data.Model}, it will automatically create a Field instance for each field configured in a {@link
 * Ext.data.Model Model}. For example, we might set up a model like this:
 *
 *     Ext.define('User', {
 *         extend: 'Ext.data.Model',
 *         fields: [
 *             'name', 'email',
 *             {name: 'age', type: 'int'},
 *             {name: 'gender', type: 'string', defaultValue: 'Unknown'}
 *         ]
 *     });
 *
 * Four fields will have been created for the User Model - name, email, age and gender. Note that we specified a couple
 * of different formats here; if we only pass in the string name of the field (as with name and email), the field is set
 * up with the 'auto' type. It's as if we'd done this instead:
 *
 *     Ext.define('User', {
 *         extend: 'Ext.data.Model',
 *         fields: [
 *             {name: 'name', type: 'auto'},
 *             {name: 'email', type: 'auto'},
 *             {name: 'age', type: 'int'},
 *             {name: 'gender', type: 'string', defaultValue: 'Unknown'}
 *         ]
 *     });
 *
 * # Types and conversion
 *
 * The {@link #type} is important - it's used to automatically convert data passed to the field into the correct format.
 * In our example above, the name and email fields used the 'auto' type and will just accept anything that is passed
 * into them. The 'age' field had an 'int' type however, so if we passed 25.4 this would be rounded to 25.
 *
 * Sometimes a simple type isn't enough, or we want to perform some processing when we load a Field's data. We can do
 * this using a {@link #convert} function. Here, we're going to create a new field based on another:
 *
 *     Ext.define('User', {
 *         extend: 'Ext.data.Model',
 *         fields: [
 *             'name', 'email',
 *             {name: 'age', type: 'int'},
 *             {name: 'gender', type: 'string', defaultValue: 'Unknown'},
 *
 *             {
 *                 name: 'firstName',
 *                 convert: function(value, record) {
 *                     var fullName  = record.get('name'),
 *                         splits    = fullName.split(" "),
 *                         firstName = splits[0];
 *
 *                     return firstName;
 *                 }
 *             }
 *         ]
 *     });
 *
 * Now when we create a new User, the firstName is populated automatically based on the name:
 *
 *     var ed = Ext.create('User', {name: 'Ed Spencer'});
 *
 *     console.log(ed.get('firstName')); 
 *
 * In fact, if we log out all of the data inside ed, we'll see this:
 *
 *     console.log(ed.data);
 *
 *     //outputs this:
 *     {
 *         age: 0,
 *         email: "",
 *         firstName: "Ed",
 *         gender: "Unknown",
 *         name: "Ed Spencer"
 *     }
 *
 * The age field has been given a default of zero because we made it an int type. As an auto field, email has defaulted
 * to an empty string. When we registered the User model we set gender's {@link #defaultValue} to 'Unknown' so we see
 * that now. Let's correct that and satisfy ourselves that the types work as we expect:
 *
 *     ed.set('gender', 'Male');
 *     ed.get('gender'); //returns 'Male'
 *
 *     ed.set('age', 25.4);
 *     ed.get('age'); //returns 25 - we wanted an int, not a float, so no decimal places allowed
 */
Ext.define('Ext.data.Field', {
    requires: ['Ext.data.Types', 'Ext.data.SortTypes'],
    alias: 'data.field',
    
    constructor : function(config) {
        if (Ext.isString(config)) {
            config = {name: config};
        }
        Ext.apply(this, config);
        
        var types = Ext.data.Types,
            st = this.sortType,
            t;

        if (this.type) {
            if (Ext.isString(this.type)) {
                this.type = types[this.type.toUpperCase()] || types.AUTO;
            }
        } else {
            this.type = types.AUTO;
        }

        // named sortTypes are supported, here we look them up
        if (Ext.isString(st)) {
            this.sortType = Ext.data.SortTypes[st];
        } else if(Ext.isEmpty(st)) {
            this.sortType = this.type.sortType;
        }

        if (!this.convert) {
            this.convert = this.type.convert;
        }
    },
    
    /**
     * @cfg {String} name
     *
     * The name by which the field is referenced within the Model. This is referenced by, for example, the `dataIndex`
     * property in column definition objects passed to Ext.grid.property.HeaderContainer.
     *
     * Note: In the simplest case, if no properties other than `name` are required, a field definition may consist of
     * just a String for the field name.
     */
    
    /**
     * @cfg {String/Object} type
     *
     * The data type for automatic conversion from received data to the *stored* value if
     * `{@link Ext.data.Field#convert convert}` has not been specified. This may be specified as a string value.
     * Possible values are
     *
     * - auto (Default, implies no conversion)
     * - string
     * - int
     * - float
     * - boolean
     * - date
     *
     * This may also be specified by referencing a member of the {@link Ext.data.Types} class.
     *
     * Developers may create their own application-specific data types by defining new members of the {@link
     * Ext.data.Types} class.
     */
    
    /**
     * @cfg {Function} convert
     *
     * A function which converts the value provided by the Reader into an object that will be stored in the Model.
     * It is passed the following parameters:
     *
     * - **v** : Mixed
     *
     *   The data value as read by the Reader, if undefined will use the configured `{@link Ext.data.Field#defaultValue
     *   defaultValue}`.
     *
     * - **rec** : Ext.data.Model
     *
     *   The data object containing the Model as read so far by the Reader. Note that the Model may not be fully populated
     *   at this point as the fields are read in the order that they are defined in your
     *   {@link Ext.data.Model#fields fields} array.
     *
     * Example of convert functions:
     *
     *     function fullName(v, record){
     *         return record.name.last + ', ' + record.name.first;
     *     }
     *
     *     function location(v, record){
     *         return !record.city ? '' : (record.city + ', ' + record.state);
     *     }
     *
     *     Ext.define('Dude', {
     *         extend: 'Ext.data.Model',
     *         fields: [
     *             {name: 'fullname',  convert: fullName},
     *             {name: 'firstname', mapping: 'name.first'},
     *             {name: 'lastname',  mapping: 'name.last'},
     *             {name: 'city', defaultValue: 'homeless'},
     *             'state',
     *             {name: 'location',  convert: location}
     *         ]
     *     });
     *
     *     // create the data store
     *     var store = Ext.create('Ext.data.Store', {
     *         reader: {
     *             type: 'json',
     *             model: 'Dude',
     *             idProperty: 'key',
     *             root: 'daRoot',
     *             totalProperty: 'total'
     *         }
     *     });
     *
     *     var myData = [
     *         { key: 1,
     *           name: { first: 'Fat',    last:  'Albert' }
     *           // notice no city, state provided in data object
     *         },
     *         { key: 2,
     *           name: { first: 'Barney', last:  'Rubble' },
     *           city: 'Bedrock', state: 'Stoneridge'
     *         },
     *         { key: 3,
     *           name: { first: 'Cliff',  last:  'Claven' },
     *           city: 'Boston',  state: 'MA'
     *         }
     *     ];
     */

    /**
     * @cfg {String} dateFormat
     *
     * Used when converting received data into a Date when the {@link #type} is specified as `"date"`.
     *
     * A format string for the {@link Ext.Date#parse Ext.Date.parse} function, or "timestamp" if the value provided by
     * the Reader is a UNIX timestamp, or "time" if the value provided by the Reader is a javascript millisecond
     * timestamp. See {@link Ext.Date}.
     */
    dateFormat: null,
    
    /**
     * @cfg {Boolean} useNull
     *
     * Use when converting received data into a Number type (either int or float). If the value cannot be
     * parsed, null will be used if useNull is true, otherwise the value will be 0. Defaults to false.
     */
    useNull: false,
    
    /**
     * @cfg {Object} defaultValue
     *
     * The default value used **when a Model is being created by a {@link Ext.data.reader.Reader Reader}**
     * when the item referenced by the `{@link Ext.data.Field#mapping mapping}` does not exist in the data object
     * (i.e. undefined). Defaults to "".
     */
    defaultValue: "",

    /**
     * @cfg {String/Number} mapping
     *
     * (Optional) A path expression for use by the {@link Ext.data.reader.Reader} implementation that is creating the
     * {@link Ext.data.Model Model} to extract the Field value from the data object. If the path expression is the same
     * as the field name, the mapping may be omitted.
     *
     * The form of the mapping expression depends on the Reader being used.
     *
     * - {@link Ext.data.reader.Json}
     *
     *   The mapping is a string containing the javascript expression to reference the data from an element of the data
     *   item's {@link Ext.data.reader.Json#root root} Array. Defaults to the field name.
     *
     * - {@link Ext.data.reader.Xml}
     *
     *   The mapping is an {@link Ext.DomQuery} path to the data item relative to the DOM element that represents the
     *   {@link Ext.data.reader.Xml#record record}. Defaults to the field name.
     *
     * - {@link Ext.data.reader.Array}
     *
     *   The mapping is a number indicating the Array index of the field's value. Defaults to the field specification's
     *   Array position.
     *
     * If a more complex value extraction strategy is required, then configure the Field with a {@link #convert}
     * function. This is passed the whole row object, and may interrogate it in whatever way is necessary in order to
     * return the desired data.
     */
    mapping: null,

    
    sortType : null,

    
    sortDir : "ASC",

    
    allowBlank : true,

    
    persist: true
});


Ext.define('Ext.data.NodeInterface', {
    requires: ['Ext.data.Field'],
    
    alternateClassName: 'Ext.data.Node',

    

    

    

    

    

    

    statics: {
        
        decorate: function(record) {
            if (!record.isNode) {
                
                
                var mgr = Ext.ModelManager,
                    modelName = record.modelName,
                    modelClass = mgr.getModel(modelName),
                    idName = modelClass.prototype.idProperty,
                    newFields = [],
                    i, newField, len;

                
                modelClass.override(this.getPrototypeBody());
                newFields = this.applyFields(modelClass, [
                    {name: idName,       type: 'string',  defaultValue: null},
                    {name: 'parentId',   type: 'string',  defaultValue: null},
                    {name: 'index',      type: 'int',     defaultValue: null},
                    {name: 'depth',      type: 'int',     defaultValue: 0},
                    {name: 'expanded',   type: 'bool',    defaultValue: false, persist: false},
                    {name: 'expandable', type: 'bool',    defaultValue: true, persist: false},
                    {name: 'checked',    type: 'auto',    defaultValue: null},
                    {name: 'leaf',       type: 'bool',    defaultValue: false, persist: false},
                    {name: 'cls',        type: 'string',  defaultValue: null, persist: false},
                    {name: 'iconCls',    type: 'string',  defaultValue: null, persist: false},
                    {name: 'root',       type: 'boolean', defaultValue: false, persist: false},
                    {name: 'isLast',     type: 'boolean', defaultValue: false, persist: false},
                    {name: 'isFirst',    type: 'boolean', defaultValue: false, persist: false},
                    {name: 'allowDrop',  type: 'boolean', defaultValue: true, persist: false},
                    {name: 'allowDrag',  type: 'boolean', defaultValue: true, persist: false},
                    {name: 'loaded',     type: 'boolean', defaultValue: false, persist: false},
                    {name: 'loading',    type: 'boolean', defaultValue: false, persist: false},
                    {name: 'href',       type: 'string',  defaultValue: null, persist: false},
                    {name: 'hrefTarget', type: 'string',  defaultValue: null, persist: false},
                    {name: 'qtip',       type: 'string',  defaultValue: null, persist: false},
                    {name: 'qtitle',     type: 'string',  defaultValue: null, persist: false}
                ]);

                len = newFields.length;
                
                for (i = 0; i < len; ++i) {
                    newField = newFields[i];
                    if (record.get(newField.name) === undefined) {
                        record.data[newField.name] = newField.defaultValue;
                    }
                }
            }

            Ext.applyIf(record, {
                firstChild: null,
                lastChild: null,
                parentNode: null,
                previousSibling: null,
                nextSibling: null,
                childNodes: []
            });
            
            record.commit(true);

            record.enableBubble([
                
                "append",

                
                "remove",

                
                "move",

                
                "insert",

                
                "beforeappend",

                
                "beforeremove",

                
                "beforemove",

                 
                "beforeinsert",

                
                "expand",

                
                "collapse",

                
                "beforeexpand",

                
                "beforecollapse",

                
                "sort"
            ]);

            return record;
        },

        applyFields: function(modelClass, addFields) {
            var modelPrototype = modelClass.prototype,
                fields = modelPrototype.fields,
                keys = fields.keys,
                ln = addFields.length,
                addField, i, name,
                newFields = [];

            for (i = 0; i < ln; i++) {
                addField = addFields[i];
                if (!Ext.Array.contains(keys, addField.name)) {
                    addField = Ext.create('data.field', addField);

                    newFields.push(addField);
                    fields.add(addField);
                }
            }

            return newFields;
        },

        getPrototypeBody: function() {
            return {
                isNode: true,

                
                createNode: function(node) {
                    if (Ext.isObject(node) && !node.isModel) {
                        node = Ext.ModelManager.create(node, this.modelName);
                    }
                    
                    return Ext.data.NodeInterface.decorate(node);
                },

                
                isLeaf : function() {
                    return this.get('leaf') === true;
                },

                
                setFirstChild : function(node) {
                    this.firstChild = node;
                },

                
                setLastChild : function(node) {
                    this.lastChild = node;
                },

                
                updateInfo: function(silent) {
                    var me = this,
                        isRoot = me.isRoot(),
                        parentNode = me.parentNode,
                        isFirst = (!parentNode ? true : parentNode.firstChild == me),
                        isLast = (!parentNode ? true : parentNode.lastChild == me),
                        depth = 0,
                        parent = me,
                        children = me.childNodes,
                        len = children.length,
                        i = 0;

                    while (parent.parentNode) {
                        ++depth;
                        parent = parent.parentNode;
                    }

                    me.beginEdit();
                    me.set({
                        isFirst: isFirst,
                        isLast: isLast,
                        depth: depth,
                        index: parentNode ? parentNode.indexOf(me) : 0,
                        parentId: parentNode ? parentNode.getId() : null
                    });
                    me.endEdit(silent);
                    if (silent) {
                        me.commit(silent);
                    }

                    for (i = 0; i < len; i++) {
                        children[i].updateInfo(silent);
                    }
                },

                
                isLast : function() {
                   return this.get('isLast');
                },

                
                isFirst : function() {
                   return this.get('isFirst');
                },

                
                hasChildNodes : function() {
                    return !this.isLeaf() && this.childNodes.length > 0;
                },

                
                isExpandable : function() {
                    var me = this;

                    if (me.get('expandable')) {
                        return !(me.isLeaf() || (me.isLoaded() && !me.hasChildNodes()));
                    }
                    return false;
                },

                
                appendChild : function(node, suppressEvents, suppressNodeUpdate) {
                    var me = this,
                        i, ln,
                        index,
                        oldParent,
                        ps;

                    
                    if (Ext.isArray(node)) {
                        for (i = 0, ln = node.length; i < ln; i++) {
                            me.appendChild(node[i]);
                        }
                    } else {
                        
                        node = me.createNode(node);

                        if (suppressEvents !== true && me.fireEvent("beforeappend", me, node) === false) {
                            return false;
                        }

                        index = me.childNodes.length;
                        oldParent = node.parentNode;

                        
                        if (oldParent) {
                            if (suppressEvents !== true && node.fireEvent("beforemove", node, oldParent, me, index) === false) {
                                return false;
                            }
                            oldParent.removeChild(node, null, false, true);
                        }

                        index = me.childNodes.length;
                        if (index === 0) {
                            me.setFirstChild(node);
                        }

                        me.childNodes.push(node);
                        node.parentNode = me;
                        node.nextSibling = null;

                        me.setLastChild(node);

                        ps = me.childNodes[index - 1];
                        if (ps) {
                            node.previousSibling = ps;
                            ps.nextSibling = node;
                            ps.updateInfo(suppressNodeUpdate);
                        } else {
                            node.previousSibling = null;
                        }

                        node.updateInfo(suppressNodeUpdate);

                        
                        if (!me.isLoaded()) {
                            me.set('loaded', true);
                        }
                        
                        else if (me.childNodes.length === 1) {
                            me.set('loaded', me.isLoaded());
                        }

                        if (suppressEvents !== true) {
                            me.fireEvent("append", me, node, index);

                            if (oldParent) {
                                node.fireEvent("move", node, oldParent, me, index);
                            }
                        }

                        return node;
                    }
                },

                
                getBubbleTarget: function() {
                    return this.parentNode;
                },

                
                removeChild : function(node, destroy, suppressEvents, suppressNodeUpdate) {
                    var me = this,
                        index = me.indexOf(node);

                    if (index == -1 || (suppressEvents !== true && me.fireEvent("beforeremove", me, node) === false)) {
                        return false;
                    }

                    
                    Ext.Array.erase(me.childNodes, index, 1);

                    
                    if (me.firstChild == node) {
                        me.setFirstChild(node.nextSibling);
                    }
                    if (me.lastChild == node) {
                        me.setLastChild(node.previousSibling);
                    }

                    
                    if (node.previousSibling) {
                        node.previousSibling.nextSibling = node.nextSibling;
                        node.previousSibling.updateInfo(suppressNodeUpdate);
                    }
                    if (node.nextSibling) {
                        node.nextSibling.previousSibling = node.previousSibling;
                        node.nextSibling.updateInfo(suppressNodeUpdate);
                    }

                    if (suppressEvents !== true) {
                        me.fireEvent("remove", me, node);
                    }


                    
                    if (!me.childNodes.length) {
                        me.set('loaded', me.isLoaded());
                    }

                    if (destroy) {
                        node.destroy(true);
                    } else {
                        node.clear();
                    }

                    return node;
                },

                
                copy: function(newId, deep) {
                    var me = this,
                        result = me.callOverridden(arguments),
                        len = me.childNodes ? me.childNodes.length : 0,
                        i;

                    
                    if (deep) {
                        for (i = 0; i < len; i++) {
                            result.appendChild(me.childNodes[i].copy(true));
                        }
                    }
                    return result;
                },

                
                clear : function(destroy) {
                    var me = this;

                    
                    me.parentNode = me.previousSibling = me.nextSibling = null;
                    if (destroy) {
                        me.firstChild = me.lastChild = null;
                    }
                },

                
                destroy : function(silent) {
                    
                    var me = this,
                        options = me.destroyOptions;

                    if (silent === true) {
                        me.clear(true);
                        Ext.each(me.childNodes, function(n) {
                            n.destroy(true);
                        });
                        me.childNodes = null;
                        delete me.destroyOptions;
                        me.callOverridden([options]);
                    } else {
                        me.destroyOptions = silent;
                        
                        me.remove(true);
                    }
                },

                
                insertBefore : function(node, refNode, suppressEvents) {
                    var me = this,
                        index     = me.indexOf(refNode),
                        oldParent = node.parentNode,
                        refIndex  = index,
                        ps;

                    if (!refNode) { 
                        return me.appendChild(node);
                    }

                    
                    if (node == refNode) {
                        return false;
                    }

                    
                    node = me.createNode(node);

                    if (suppressEvents !== true && me.fireEvent("beforeinsert", me, node, refNode) === false) {
                        return false;
                    }

                    
                    if (oldParent == me && me.indexOf(node) < index) {
                        refIndex--;
                    }

                    
                    if (oldParent) {
                        if (suppressEvents !== true && node.fireEvent("beforemove", node, oldParent, me, index, refNode) === false) {
                            return false;
                        }
                        oldParent.removeChild(node);
                    }

                    if (refIndex === 0) {
                        me.setFirstChild(node);
                    }

                    Ext.Array.splice(me.childNodes, refIndex, 0, node);
                    node.parentNode = me;

                    node.nextSibling = refNode;
                    refNode.previousSibling = node;

                    ps = me.childNodes[refIndex - 1];
                    if (ps) {
                        node.previousSibling = ps;
                        ps.nextSibling = node;
                        ps.updateInfo();
                    } else {
                        node.previousSibling = null;
                    }

                    node.updateInfo();

                    if (!me.isLoaded()) {
                        me.set('loaded', true);
                    }
                    
                    else if (me.childNodes.length === 1) {
                        me.set('loaded', me.isLoaded());
                    }

                    if (suppressEvents !== true) {
                        me.fireEvent("insert", me, node, refNode);

                        if (oldParent) {
                            node.fireEvent("move", node, oldParent, me, refIndex, refNode);
                        }
                    }

                    return node;
                },

                
                insertChild: function(index, node) {
                    var sibling = this.childNodes[index];
                    if (sibling) {
                        return this.insertBefore(node, sibling);
                    }
                    else {
                        return this.appendChild(node);
                    }
                },

                
                remove : function(destroy, suppressEvents) {
                    var parentNode = this.parentNode;

                    if (parentNode) {
                        parentNode.removeChild(this, destroy, suppressEvents, true);
                    }
                    return this;
                },

                
                removeAll : function(destroy, suppressEvents) {
                    var cn = this.childNodes,
                        n;

                    while ((n = cn[0])) {
                        this.removeChild(n, destroy, suppressEvents);
                    }
                    return this;
                },

                
                getChildAt : function(index) {
                    return this.childNodes[index];
                },

                
                replaceChild : function(newChild, oldChild, suppressEvents) {
                    var s = oldChild ? oldChild.nextSibling : null;

                    this.removeChild(oldChild, suppressEvents);
                    this.insertBefore(newChild, s, suppressEvents);
                    return oldChild;
                },

                
                indexOf : function(child) {
                    return Ext.Array.indexOf(this.childNodes, child);
                },

                
                getPath: function(field, separator) {
                    field = field || this.idProperty;
                    separator = separator || '/';

                    var path = [this.get(field)],
                        parent = this.parentNode;

                    while (parent) {
                        path.unshift(parent.get(field));
                        parent = parent.parentNode;
                    }
                    return separator + path.join(separator);
                },

                
                getDepth : function() {
                    return this.get('depth');
                },

                
                bubble : function(fn, scope, args) {
                    var p = this;
                    while (p) {
                        if (fn.apply(scope || p, args || [p]) === false) {
                            break;
                        }
                        p = p.parentNode;
                    }
                },


                
                cascadeBy : function(fn, scope, args) {
                    if (fn.apply(scope || this, args || [this]) !== false) {
                        var childNodes = this.childNodes,
                            length     = childNodes.length,
                            i;

                        for (i = 0; i < length; i++) {
                            childNodes[i].cascadeBy(fn, scope, args);
                        }
                    }
                },

                
                eachChild : function(fn, scope, args) {
                    var childNodes = this.childNodes,
                        length     = childNodes.length,
                        i;

                    for (i = 0; i < length; i++) {
                        if (fn.apply(scope || this, args || [childNodes[i]]) === false) {
                            break;
                        }
                    }
                },

                
                findChild : function(attribute, value, deep) {
                    return this.findChildBy(function() {
                        return this.get(attribute) == value;
                    }, null, deep);
                },

                
                findChildBy : function(fn, scope, deep) {
                    var cs = this.childNodes,
                        len = cs.length,
                        i = 0, n, res;

                    for (; i < len; i++) {
                        n = cs[i];
                        if (fn.call(scope || n, n) === true) {
                            return n;
                        }
                        else if (deep) {
                            res = n.findChildBy(fn, scope, deep);
                            if (res !== null) {
                                return res;
                            }
                        }
                    }

                    return null;
                },

                
                contains : function(node) {
                    return node.isAncestor(this);
                },

                
                isAncestor : function(node) {
                    var p = this.parentNode;
                    while (p) {
                        if (p == node) {
                            return true;
                        }
                        p = p.parentNode;
                    }
                    return false;
                },

                
                sort : function(sortFn, recursive, suppressEvent) {
                    var cs  = this.childNodes,
                        ln = cs.length,
                        i, n;

                    if (ln > 0) {
                        Ext.Array.sort(cs, sortFn);
                        for (i = 0; i < ln; i++) {
                            n = cs[i];
                            n.previousSibling = cs[i-1];
                            n.nextSibling = cs[i+1];

                            if (i === 0) {
                                this.setFirstChild(n);
                                n.updateInfo();
                            }
                            if (i == ln - 1) {
                                this.setLastChild(n);
                                n.updateInfo();
                            }
                            if (recursive && !n.isLeaf()) {
                                n.sort(sortFn, true, true);
                            }
                        }

                        if (suppressEvent !== true) {
                            this.fireEvent('sort', this, cs);
                        }
                    }
                },

                
                isExpanded: function() {
                    return this.get('expanded');
                },

                
                isLoaded: function() {
                    return this.get('loaded');
                },

                
                isLoading: function() {
                    return this.get('loading');
                },

                
                isRoot: function() {
                    return !this.parentNode;
                },

                
                isVisible: function() {
                    var parent = this.parentNode;
                    while (parent) {
                        if (!parent.isExpanded()) {
                            return false;
                        }
                        parent = parent.parentNode;
                    }
                    return true;
                },

                
                expand: function(recursive, callback, scope) {
                    var me = this;

                    
                    

                    
                    if (!me.isLeaf()) {
                        
                        if (me.isLoading()) {
                            me.on('expand', function(){
                                me.expand(recursive, callback, scope);
                            }, me, {single: true});
                        } else {
                            
                            if (!me.isExpanded()) {
                                
                                
                                
                                
                                me.fireEvent('beforeexpand', me, function(){
                                    me.set('expanded', true);
                                    me.fireEvent('expand', me, me.childNodes, false);

                                    
                                    if (recursive) {
                                        me.expandChildren(true, callback, scope);
                                    } else {
                                        Ext.callback(callback, scope || me, [me.childNodes]);
                                    }
                                }, me);
                            } else if (recursive) {
                                
                                me.expandChildren(true, callback, scope);
                            } else {
                                Ext.callback(callback, scope || me, [me.childNodes]);
                            }
                        }
                    } else {
                        
                        Ext.callback(callback, scope || me); 
                    }
                },

                
                expandChildren: function(recursive, callback, scope) {
                    var me = this,
                        i = 0,
                        nodes = me.childNodes,
                        ln = nodes.length,
                        node,
                        expanding = 0;

                    for (; i < ln; ++i) {
                        node = nodes[i];
                        if (!node.isLeaf() && !node.isExpanded()) {
                            expanding++;
                            nodes[i].expand(recursive, function () {
                                expanding--;
                                if (callback && !expanding) {
                                    Ext.callback(callback, scope || me, [me.childNodes]);
                                }
                            });
                        }
                    }

                    if (!expanding && callback) {
                        Ext.callback(callback, scope || me, [me.childNodes]);                    }
                },

                
                collapse: function(recursive, callback, scope) {
                    var me = this;

                    
                    if (!me.isLeaf()) {
                        
                        if (!me.collapsing && me.isExpanded()) {
                            me.fireEvent('beforecollapse', me, function() {
                                me.set('expanded', false);
                                me.fireEvent('collapse', me, me.childNodes, false);

                                
                                if (recursive) {
                                    me.collapseChildren(true, callback, scope);
                                }
                                else {
                                    Ext.callback(callback, scope || me, [me.childNodes]);
                                }
                            }, me);
                        }
                        
                        else if (recursive) {
                            me.collapseChildren(true, callback, scope);
                        }
                    }
                    
                    else {
                        Ext.callback(callback, scope || me, [me.childNodes]);
                    }
                },

                
                collapseChildren: function(recursive, callback, scope) {
                    var me = this,
                        i = 0,
                        nodes = me.childNodes,
                        ln = nodes.length,
                        node,
                        collapsing = 0;

                    for (; i < ln; ++i) {
                        node = nodes[i];
                        if (!node.isLeaf() && node.isExpanded()) {
                            collapsing++;
                            nodes[i].collapse(recursive, function () {
                                collapsing--;
                                if (callback && !collapsing) {
                                    Ext.callback(callback, scope || me, [me.childNodes]);
                                }
                            });
                        }
                    }

                    if (!collapsing && callback) {
                        Ext.callback(callback, scope || me, [me.childNodes]);
                    }
                }
            };
        }
    }
});

Ext.define('Ext.data.association.HasMany', {
    extend: 'Ext.data.association.Association',
    alternateClassName: 'Ext.data.HasManyAssociation',
    requires: ['Ext.util.Inflector'],

    alias: 'association.hasmany',

    
    
    
    
    
    
    
    
    
    
    
    
    constructor: function(config) {
        var me = this,
            ownerProto,
            name;
            
        me.callParent(arguments);
        
        me.name = me.name || Ext.util.Inflector.pluralize(me.associatedName.toLowerCase());
        
        ownerProto = me.ownerModel.prototype;
        name = me.name;
        
        Ext.applyIf(me, {
            storeName : name + "Store",
            foreignKey: me.ownerName.toLowerCase() + "_id"
        });
        
        ownerProto[name] = me.createStore();
    },
    
    
    createStore: function() {
        var that            = this,
            associatedModel = that.associatedModel,
            storeName       = that.storeName,
            foreignKey      = that.foreignKey,
            primaryKey      = that.primaryKey,
            filterProperty  = that.filterProperty,
            autoLoad        = that.autoLoad,
            storeConfig     = that.storeConfig || {};
        
        return function() {
            var me = this,
                config, filter,
                modelDefaults = {};
                
            if (me[storeName] === undefined) {
                if (filterProperty) {
                    filter = {
                        property  : filterProperty,
                        value     : me.get(filterProperty),
                        exactMatch: true
                    };
                } else {
                    filter = {
                        property  : foreignKey,
                        value     : me.get(primaryKey),
                        exactMatch: true
                    };
                }
                
                modelDefaults[foreignKey] = me.get(primaryKey);
                
                config = Ext.apply({}, storeConfig, {
                    model        : associatedModel,
                    filters      : [filter],
                    remoteFilter : false,
                    modelDefaults: modelDefaults
                });
                
                me[storeName] = Ext.create('Ext.data.Store', config);
                if (autoLoad) {
                    me[storeName].load();
                }
            }
            
            return me[storeName];
        };
    },
    
    
    read: function(record, reader, associationData){
        var store = record[this.name](),
            inverse;
    
        store.add(reader.read(associationData).records);
    
        
        
        inverse = this.associatedModel.prototype.associations.findBy(function(assoc){
            return assoc.type === 'belongsTo' && assoc.associatedName === record.$className;
        });
    
        
        if (inverse) {
            store.data.each(function(associatedRecord){
                associatedRecord[inverse.instanceName] = record;
            });
        }
    }
});

Ext.define('Ext.data.reader.Json', {
    extend: 'Ext.data.reader.Reader',
    alternateClassName: 'Ext.data.JsonReader',
    alias : 'reader.json',

    root: '',

    

    
    useSimpleAccessors: false,

    
    readRecords: function(data) {
        
        if (data.metaData) {
            this.onMetaChange(data.metaData);
        }

        
        this.jsonData = data;
        return this.callParent([data]);
    },

    
    getResponseData: function(response) {
        try {
            var data = Ext.decode(response.responseText);
        }
        catch (ex) {
            Ext.Error.raise({
                response: response,
                json: response.responseText,
                parseError: ex,
                msg: 'Unable to parse the JSON returned by the server: ' + ex.toString()
            });
        }
        if (!data) {
            Ext.Error.raise('JSON object not found');
        }

        return data;
    },

    
    buildExtractors : function() {
        var me = this;

        me.callParent(arguments);

        if (me.root) {
            me.getRoot = me.createAccessor(me.root);
        } else {
            me.getRoot = function(root) {
                return root;
            };
        }
    },

    
    extractData: function(root) {
        var recordName = this.record,
            data = [],
            length, i;

        if (recordName) {
            length = root.length;

            for (i = 0; i < length; i++) {
                data[i] = root[i][recordName];
            }
        } else {
            data = root;
        }
        return this.callParent([data]);
    },

    
    createAccessor: function() {
        var re = /[\[\.]/;

        return function(expr) {
            if (Ext.isEmpty(expr)) {
                return Ext.emptyFn;
            }
            if (Ext.isFunction(expr)) {
                return expr;
            }
            if (this.useSimpleAccessors !== true) {
                var i = String(expr).search(re);
                if (i >= 0) {
                    return Ext.functionFactory('obj', 'return obj' + (i > 0 ? '.' : '') + expr);
                }
            }
            return function(obj) {
                return obj[expr];
            };
        };
    }()
});

Ext.define('Ext.data.reader.Array', {
    extend: 'Ext.data.reader.Json',
    alternateClassName: 'Ext.data.ArrayReader',
    alias : 'reader.array',

    
    buildExtractors: function() {
        this.callParent(arguments);
        
        var fields = this.model.prototype.fields.items,
            i = 0,
            length = fields.length,
            extractorFunctions = [],
            map;
        
        for (; i < length; i++) {
            map = fields[i].mapping;
            extractorFunctions.push(function(index) {
                return function(data) {
                    return data[index];
                };
            }(map !== null ? map : i));
        }
        
        this.extractorFunctions = extractorFunctions;
    }
});


Ext.define('Ext.data.writer.Json', {
    extend: 'Ext.data.writer.Writer',
    alternateClassName: 'Ext.data.JsonWriter',
    alias: 'writer.json',
    
    
    root: undefined,
    
    
    encode: false,
    
    
    allowSingle: true,
    
    
    writeRecords: function(request, data) {
        var root = this.root;
        
        if (this.allowSingle && data.length == 1) {
            
            data = data[0];
        }
        
        if (this.encode) {
            if (root) {
                
                request.params[root] = Ext.encode(data);
            } else {
                Ext.Error.raise('Must specify a root when using encode');
            }
        } else {
            
            request.jsonData = request.jsonData || {};
            if (root) {
                request.jsonData[root] = data;
            } else {
                request.jsonData = data;
            }
        }
        return request;
    }
});

Ext.ns('Ext.util');


Ext.util.DelayedTask = function(fn, scope, args) {
    var me = this,
        id,
        call = function() {
            clearInterval(id);
            id = null;
            fn.apply(scope, args || []);
        };

    
    this.delay = function(delay, newFn, newScope, newArgs) {
        me.cancel();
        fn = newFn || fn;
        scope = newScope || scope;
        args = newArgs || args;
        id = setInterval(call, delay);
    };

    
    this.cancel = function(){
        if (id) {
            clearInterval(id);
            id = null;
        }
    };
};


Ext.define('Ext.util.Grouper', {

    

    extend: 'Ext.util.Sorter',

    

    
    getGroupString: function(instance) {
        return instance.get(this.property);
    }
});

Ext.define('Ext.util.Point', {

    requires: ['Ext.Validator'],

    radianToDegreeConstant: 180 / Math.PI,

    statics: {
        
        fromEvent: function(e) {
            var changedTouches = e.changedTouches,
                touch = (changedTouches && changedTouches.length > 0) ? changedTouches[0] : e;

            return this.fromTouch(touch);
        },

        
        fromTouch: function(touch) {
            return new this(touch.pageX, touch.pageY);
        },

        validate: function(point) {
            if (!point || !('x' in point) || !('y' in point)) {
                throw new Error("[" + Ext.getDisplayName(this.validate.caller) + "] Invalid point, must be either an instance of Ext.util.Point or an object with 'x' and 'y' properties");
            }

            Ext.Validator.number(point.x);
            Ext.Validator.number(point.y);
        }
    },

    constructor: function(x, y) {
        if (typeof x == 'undefined') {
            x = 0;
        }

        if (typeof y == 'undefined') {
            y = 0;
        }

        Ext.Validator.number(x);
        Ext.Validator.number(y);

        this.x = x;
        this.y = y;

        return this;
    },

    
    clone: function() {
        return new this.self(this.x, this.y);
    },

    
    copy: function() {
        return this.clone.apply(this, arguments);
    },

    
    copyFrom: function(point) {
        this.statics().validate(point);

        this.x = point.x;
        this.y = point.y;

        return this;
    },

    
    toString: function() {
        return "Point[" + this.x + "," + this.y + "]";
    },

    
    equals: function(point) {
        this.statics().validate(point);

        return (this.x === point.x && this.y === point.y);
    },

    
    isCloseTo: function(point, threshold) {
        this.statics().validate(point);

        if (typeof threshold == 'number') {
            threshold = {x: threshold};
            threshold.y = threshold.x;
        }

        var x = point.x,
            y = point.y,
            thresholdX = threshold.x,
            thresholdY = threshold.y;

        return (this.x <= x + thresholdX && this.x >= x - thresholdX &&
                this.y <= y + thresholdY && this.y >= y - thresholdY);
    },

    
    isWithin: function() {
        return this.isCloseTo.apply(this, arguments);
    },

    
    translate: function(x, y) {
        Ext.Validator.number(x);
        Ext.Validator.number(y);

        this.x += x;
        this.y += y;

        return this;
    },

    
    roundedEquals: function(point) {
        this.statics().validate(point);

        return (Math.round(this.x) === Math.round(point.x) &&
                Math.round(this.y) === Math.round(point.y));
    },

    getDistanceTo: function(point) {
        this.statics().validate(point);

        var deltaX = this.x - point.x,
            deltaY = this.y - point.y;

        return Math.sqrt(deltaX * deltaX + deltaY * deltaY);
    },

    getAngleTo: function(point) {
        this.statics().validate(point);

        var deltaX = this.x - point.x,
            deltaY = this.y - point.y;

        return Math.atan2(deltaY, deltaX) * this.radianToDegreeConstant;
    }
});


Ext.define("Ext.util.Sortable", {
    
    isSortable: true,
    
    
    defaultSortDirection: "ASC",
    
    requires: [
        'Ext.util.Sorter'
    ],

        
    
    
    initSortable: function() {
        var me = this,
            sorters = me.sorters;
        
        
        me.sorters = Ext.create('Ext.util.AbstractMixedCollection', false, function(item) {
            return item.id || item.property;
        });
        
        if (sorters) {
            me.sorters.addAll(me.decodeSorters(sorters));
        }
    },

    
    sort: function(sorters, direction, where, doSort) {
        var me = this,
            sorter, sorterFn,
            newSorters;
        
        if (Ext.isArray(sorters)) {
            doSort = where;
            where = direction;
            newSorters = sorters;
        }
        else if (Ext.isObject(sorters)) {
            doSort = where;
            where = direction;
            newSorters = [sorters];
        }
        else if (Ext.isString(sorters)) {
            sorter = me.sorters.get(sorters);

            if (!sorter) {
                sorter = {
                    property : sorters,
                    direction: direction
                };
                newSorters = [sorter];
            }
            else if (direction === undefined) {
                sorter.toggle();
            }
            else {
                sorter.setDirection(direction);
            }
        }
        
        if (newSorters && newSorters.length) {
            newSorters = me.decodeSorters(newSorters);
            if (Ext.isString(where)) {
                if (where === 'prepend') {
                    sorters = me.sorters.clone().items;
                    
                    me.sorters.clear();
                    me.sorters.addAll(newSorters);
                    me.sorters.addAll(sorters);
                }
                else {
                    me.sorters.addAll(newSorters);
                }
            }
            else {
                me.sorters.clear();
                me.sorters.addAll(newSorters);
            }
            
            if (doSort !== false) {
                me.onBeforeSort(newSorters);
            }
        }
        
        if (doSort !== false) {
            sorters = me.sorters.items;
            if (sorters.length) {
                
                sorterFn = function(r1, r2) {
                    var result = sorters[0].sort(r1, r2),
                        length = sorters.length,
                        i;

                        
                        for (i = 1; i < length; i++) {
                            result = result || sorters[i].sort.call(this, r1, r2);
                        }

                    return result;
                };

                me.doSort(sorterFn);                
            }
        }
        
        return sorters;
    },
    
    onBeforeSort: Ext.emptyFn,
        
    
    decodeSorters: function(sorters) {
        if (!Ext.isArray(sorters)) {
            if (sorters === undefined) {
                sorters = [];
            } else {
                sorters = [sorters];
            }
        }

        var length = sorters.length,
            Sorter = Ext.util.Sorter,
            fields = this.model ? this.model.prototype.fields : null,
            field,
            config, i;

        for (i = 0; i < length; i++) {
            config = sorters[i];

            if (!(config instanceof Sorter)) {
                if (Ext.isString(config)) {
                    config = {
                        property: config
                    };
                }
                
                Ext.applyIf(config, {
                    root     : this.sortRoot,
                    direction: "ASC"
                });

                
                if (config.fn) {
                    config.sorterFn = config.fn;
                }

                
                if (typeof config == 'function') {
                    config = {
                        sorterFn: config
                    };
                }

                
                if (fields && !config.transform) {
                    field = fields.get(config.property);
                    config.transform = field ? field.sortType : undefined;
                }
                sorters[i] = Ext.create('Ext.util.Sorter', config);
            }
        }

        return sorters;
    },
    
    getSorters: function() {
        return this.sorters.items;
    }
});
Ext.define('Ext.event.Dispatcher', {

    requires: [
        'Ext.event.ListenerStack',
        'Ext.event.Controller'
    ],

    statics: {
        getInstance: function() {
            if (!this.instance) {
                this.instance = new this();
            }

            return this.instance;
        },

        setInstance: function(instance) {
            this.instance = instance;

            return this;
        }
    },

    config: {
        publishers: {}
    },

    wildcard: '*',

    constructor: function(config) {
        this.listenerStacks = {};

        this.activePublishers = {};

        this.publishersCache = {};

        this.noActivePublishers = [];

        this.controller = null;

        this.initConfig(config);

        return this;
    },

    getListenerStack: function(targetType, target, eventName, createIfNotExist) {
        var listenerStacks = this.listenerStacks,
            map = listenerStacks[targetType],
            listenerStack;

        if (!map) {
            if (createIfNotExist) {
                map = listenerStacks[targetType] = {};
            }
            else {
                return null;
            }
        }

        map = map[target];

        if (!map) {
            if (createIfNotExist) {
                map = listenerStacks[targetType][target] = {};
            }
            else {
                return null;
            }
        }

        listenerStack = map[eventName];

        if (!listenerStack) {
            if (createIfNotExist) {
                listenerStack = map[eventName] = new Ext.event.ListenerStack();
            }
            else {
                return null;
            }
        }

        return listenerStack;
    },

    getController: function(targetType, target, eventName, connectedController) {
        var controller = this.controller,
            info = {
                targetType: targetType,
                target: target,
                eventName: eventName
            };

        if (!controller) {
            controller = this.controller = new Ext.event.Controller();
        }

        if (controller.isFiring) {
            controller = new Ext.event.Controller();
        }

        controller.setInfo(info);

        if (connectedController && controller !== connectedController) {
            controller.connect(connectedController);
        }

        return controller;
    },

    applyPublishers: function(publishers) {
        var i, publisher;

        this.publishersCache = {};
        
        for (i in publishers) {
            if (publishers.hasOwnProperty(i)) {
                publisher = publishers[i];

                this.registerPublisher(publisher);
            }
        }

        return publishers;
    },

    registerPublisher: function(publisher) {
        var targetType = publisher.getTargetType(),
            publishers = this.activePublishers[targetType];

        if (!publishers) {
            publishers = this.activePublishers[targetType] = [];
        }

        publishers.push(publisher);

        publisher.setDispatcher(this);

        return this;
    },

    getCachedActivePublishers: function(targetType, eventName) {
        var cache = this.publishersCache,
            publishers;

        if ((publishers = cache[targetType]) && (publishers = publishers[eventName])) {
            return publishers;
        }

        return null;
    },

    cacheActivePublishers: function(targetType, eventName, publishers) {
        var cache = this.publishersCache;

        if (!cache[targetType]) {
            cache[targetType] = {};
        }

        cache[targetType][eventName] = publishers;

        return publishers;
    },

    getActivePublishers: function(targetType, eventName) {
        var publishers, activePublishers,
            i, ln, publisher;

        if ((publishers = this.getCachedActivePublishers(targetType, eventName))) {
            return publishers;
        }

        activePublishers = this.activePublishers[targetType];

        if (activePublishers) {
            publishers = [];

            for (i = 0,ln = activePublishers.length; i < ln; i++) {
                publisher = activePublishers[i];

                if (publisher.handles(eventName)) {
                    publishers.push(publisher);
                }
            }
        }
        else {
            publishers = this.noActivePublishers;
        }

        return this.cacheActivePublishers(targetType, eventName, publishers);
    },

    hasListener: function(targetType, target, eventName) {
        var listenerStack = this.getListenerStack(targetType, target, eventName);

        if (listenerStack) {
            return listenerStack.count() > 0;
        }

        return false;
    },

    addListener: function(targetType, target, eventName) {
        var publishers = this.getActivePublishers(targetType, eventName),
            ln = publishers.length,
            i;

        if (ln > 0) {
            for (i = 0; i < ln; i++) {
                publishers[i].subscribe(target, eventName);
            }
        }

        return this.doAddListener.apply(this, arguments);
    },

    doAddListener: function(targetType, target, eventName, fn, scope, options, order) {
        var listenerStack = this.getListenerStack(targetType, target, eventName, true);

        return listenerStack.add(fn, scope, options, order);
    },

    removeListener: function(targetType, target, eventName) {
        var publishers = this.getActivePublishers(targetType, eventName),
            ln = publishers.length,
            i;

        if (ln > 0) {
            for (i = 0; i < ln; i++) {
                publishers[i].unsubscribe(target, eventName);
            }
        }

        return this.doRemoveListener.apply(this, arguments);
    },

    doRemoveListener: function(targetType, target, eventName, fn, scope, order) {
        var listenerStack = this.getListenerStack(targetType, target, eventName);

        if (listenerStack === null) {
            return false;
        }

        return listenerStack.remove(fn, scope, order);
    },

    clearListeners: function(targetType, target, eventName) {
        var listenerStacks = this.listenerStacks,
            ln = arguments.length;

        if (ln === 3) {
            if (listenerStacks[targetType] && listenerStacks[targetType][target]) {
                delete listenerStacks[targetType][target][eventName];
            }
        }
        else if (ln === 2) {
            if (listenerStacks[targetType]) {
                delete listenerStacks[targetType][target];
            }
        }
        else if (ln === 1) {
            delete listenerStacks[targetType];
        }
        else {
            delete this.listenerStacks;
            this.listenerStacks = {};
        }

        return this;
    },

    dispatchEvent: function(targetType, target, eventName) {
        var publishers = this.getActivePublishers(targetType, eventName),
            ln = publishers.length,
            i;

        if (ln > 0) {
            for (i = 0; i < ln; i++) {
                publishers[i].notify(target, eventName);
            }
        }

        return this.doDispatchEvent.apply(this, arguments);
    },

    doDispatchEvent: function(targetType, target, eventName, args, actions, connectedController) {
        var listenerStack = this.getListenerStack(targetType, target, eventName),
            wildcardStacks = this.getWildcardListenerStacks(targetType, target, eventName),
            controller;

        if (wildcardStacks.length > 0) {
            if (!actions) {
                actions = [];
            }

            actions.push({
                fn: this.fireListenerStacks,
                scope: this,
                options: {
                    args: [wildcardStacks, 0, targetType, target, eventName]
                },
                order: 'after'
            });
        }

        if ((!listenerStack || listenerStack.length == 0) && (!actions || actions.length == 0)) {
            return;
        }

        controller = this.getController(targetType, target, eventName, connectedController);
        controller.setListenerStack(listenerStack);
        controller.fire(args, actions);

        return !controller.isInterrupted();
    },

    getWildcardListenerStacks: function(targetType, target, eventName) {
        var stacks = [],
            wildcard = this.wildcard,
            isEventNameNotWildcard = eventName !== wildcard,
            isTargetNotWildcard = target !== wildcard,
            stack;

        if (isEventNameNotWildcard && (stack = this.getListenerStack(targetType, target, wildcard))) {
            stacks.push(stack);
        }

        if (isTargetNotWildcard && (stack = this.getListenerStack(targetType, wildcard, eventName))) {
            stacks.push(stack);
        }

        return stacks;
    },

    fireListenerStacks: function(listenerStacks, index, targetType, target, eventName) {
        var listenerStack = listenerStacks[index],
            ln = listenerStacks.length,
            controller = this.getController(targetType, target, eventName),
            args = Array.prototype.slice.call(arguments, 5, -2),
            actions;

        if (++index <= ln - 1) {
            actions = [{
                fn: this.fireListenerStacks,
                scope: this,
                options: {
                    args: [listenerStacks, index, targetType, target, eventName]
                },
                order: 'after'
            }];
        }

        controller.setListenerStack(listenerStack);
        controller.fire(args, actions);
    }
});


Ext.define('Ext.event.Dom', {
    extend: 'Ext.event.Event',

    constructor: function(event) {
        this.browserEvent = this.event = event;
        this.target = this.delegatedTarget = event.target;
        this.type = event.type;
        this.pageX = event.pageX;
        this.pageY = event.pageY;

        this.timeStamp = this.time = event.timeStamp;

        if (typeof this.time != 'number') {
            this.time = new Date(this.time).getTime();
        }

        return this;
    },

    stopEvent: function() {
        this.preventDefault();

        return this.callParent();
    },

    preventDefault: function() {
        this.browserEvent.preventDefault();
    },

    getPageX: function() {
        return this.browserEvent.pageX;
    },

    getPageY: function() {
        return this.browserEvent.pageY;
    },

    
    getXY: function() {
        if (!this.xy) {
            this.xy = [this.getPageX(), this.getPageY()];
        }

        return this.xy;
    },

    setDelegatedTarget: function(target) {
        this.delegatedTarget = target;
    },

    
    getTarget: function(selector, maxDepth, returnEl) {
        if (arguments.length === 0) {
            return this.delegatedTarget;
        }

        return selector ? Ext.fly(this.target).findParent(selector, maxDepth, returnEl) : (returnEl ? Ext.get(this.target) : this.target);
    },

    getTime: function() {
        return this.time;
    }
});

Ext.define('Ext.event.Touch', {
    extend: 'Ext.event.Dom',

    requires: [
        'Ext.util.Point'
    ],

    constructor: function(event, info) {
        if (info) {
            this.set(info);
        }

        this.touchesMap = {};

        this.changedTouches = this.cloneTouches(event.changedTouches);
        this.touches = this.cloneTouches(event.touches);
        this.targetTouches = this.cloneTouches(event.targetTouches);

        return this.callParent([event]);
    },

    setTargets: function(targetsMap) {
        this.doSetTargets(this.changedTouches, targetsMap);
        this.doSetTargets(this.touches, targetsMap);
        this.doSetTargets(this.targetTouches, targetsMap);
    },

    doSetTargets: function(touches, targetsMap) {
        var i, ln, touch, identifier, targets;

        for (i = 0,ln = touches.length; i < ln; i++) {
            touch = touches[i];

            identifier = touch.identifier;

            targets = targetsMap[identifier];

            if (targets) {
                touch.targets = targets;
            }
        }
    },

    cloneTouches: function(touches) {
        var map = this.touchesMap,
            clone = [],
            lastIdentifier = null,
            i, ln, touch, identifier;

        for (i = 0,ln = touches.length; i < ln; i++) {
            touch = touches[i];

            identifier = touch.identifier;

            
            
            if (lastIdentifier !== null && identifier === lastIdentifier) {
                identifier++;
            }

            lastIdentifier = identifier;

            if (!map[identifier]) {
                map[identifier] = {
                    pageX: touch.pageX,
                    pageY: touch.pageY,
                    identifier: identifier,
                    target: touch.target,
                    timeStamp: touch.timeStamp,
                    point: Ext.util.Point.fromTouch(touch)
                };
            }

            clone[i] = map[identifier];
        }

        return clone;
    }
});

Ext.define('Ext.event.publisher.ComponentPaint', {

    extend: 'Ext.event.publisher.Publisher',

    targetType: 'component',

    handledEvents: ['painted', 'erased'],

    idSelectorRegex: /^#([\w\-]+)$/i,

    eventNames: {
        painted: 'painted',
        erased: 'erased'
    },

    constructor: function() {
        this.callParent(arguments);

        this.subscribers = {};
    },

    getSubscribers: function(eventName, createIfNotExist) {
        var subscribers = this.subscribers;

        if (!subscribers.hasOwnProperty(eventName)) {
            if (!createIfNotExist) {
                return null;
            }

            subscribers[eventName] = {
                $length: 0
            };
        }

        return subscribers[eventName];
    },

    setDispatcher: function(dispatcher) {
        var targetType = this.targetType;

        dispatcher.doAddListener(targetType, '*', 'renderedchange', 'onComponentRenderedChange', this);
        dispatcher.doAddListener(targetType, '*', 'hiddenchange', 'onComponentHiddenChange', this);

        return this.callParent(arguments);
    },

    subscribe: function(target, eventName) {
        var match = target.match(this.idSelectorRegex),
            subscribers;

        if (!match) {
            return false;
        }

        subscribers = this.getSubscribers(eventName, true);
        subscribers[match[1]] = true;
        subscribers.$length++;

        return true;
    },

    unsubscribe: function(target, eventName) {
        var match = target.match(this.idSelectorRegex),
            subscribers;

        if (!match || !(subscribers = this.getSubscribers(eventName))) {
            return false;
        }

        delete subscribers[match[1]];
        subscribers.$length--;

        return true;
    },

    onComponentRenderedChange: function(component, rendered) {
        var eventNames = this.eventNames,
            eventName = rendered ? eventNames.painted : eventNames.erased,
            subscribers = this.getSubscribers(eventName);

        if (subscribers && subscribers.$length > 0) {
            this.publish(subscribers, component, eventName);
        }
    },

    onComponentHiddenChange: function(component, hidden) {
        var eventNames = this.eventNames,
            eventName = hidden ? eventNames.erased : eventNames.painted,
            subscribers = this.getSubscribers(eventName);

        if (subscribers && subscribers.$length > 0) {
            this.publish(subscribers, component, eventName);
        }
    },

    publish: function(subscribers, component, eventName) {
        var id = component.getId(),
            eventNames, items, i, ln, isPainted;

        if (subscribers[id]) {
            eventNames = this.eventNames;
            isPainted = component.isPainted();

            if ((eventName === eventNames.painted && isPainted) || eventName === eventNames.erased && !isPainted) {
                this.dispatcher.doDispatchEvent(this.targetType, '#' + id, eventName, [component]);
            }
            else {
                return this;
            }
        }

        if (component.isContainer) {
            items = component.getItems().items;

            for (i = 0,ln = items.length; i < ln; i++) {
                this.publish(subscribers, items[i], eventName);
            }
        }
    }

});


Ext.define('Ext.event.recognizer.Recognizer', {
    mixins: ['Ext.mixin.Identifiable'],

    handledEvents: [],

    config: {
        onRecognized: Ext.emptyFn,
        onFailed: Ext.emptyFn,
        callbackScope: null
    },

    constructor: function(config) {
        this.initConfig(config);

        return this;
    },

    getHandledEvents: function() {
        return this.handledEvents;
    },

    onStart: Ext.emptyFn,

    onEnd: Ext.emptyFn,

    fail: function() {
        this.getOnFailed().apply(this.getCallbackScope(), arguments);

        return false;
    },

    fire: function() {
        this.getOnRecognized().apply(this.getCallbackScope(), arguments);
    }
});

Ext.define('Ext.event.recognizer.Touch', {

    extend: 'Ext.event.recognizer.Recognizer',

    onTouchStart: Ext.emptyFn,

    onTouchMove: Ext.emptyFn,

    onTouchEnd: Ext.emptyFn
});

Ext.define('Ext.event.recognizer.MultiTouch', {
    extend: 'Ext.event.recognizer.Touch',

    requiredTouchesCount: 2,

    isTracking: false,

    isStarted: false,

    onTouchStart: function(e) {
        var requiredTouchesCount = this.requiredTouchesCount,
            touches = e.touches,
            touchesCount = touches.length;

        if (touchesCount === requiredTouchesCount) {
            this.start(e);
        }
        else if (touchesCount > requiredTouchesCount) {
            this.end(e);
        }
    },

    onTouchEnd: function(e) {
        this.end(e);
    },

    start: function() {
        if (!this.isTracking) {
            this.isTracking = true;
            this.isStarted = false;
        }
    },

    end: function(e) {
        if (this.isTracking) {
            this.isTracking = false;

            if (this.isStarted) {
                this.isStarted = false;

                this.fireEnd(e);
            }
        }
    }
});

Ext.define('Ext.event.recognizer.Pinch', {
    extend: 'Ext.event.recognizer.MultiTouch',

    requiredTouchesCount: 2,

    handledEvents: ['pinchstart', 'pinch', 'pinchend'],

    startDistance: 0,

    lastTouches: null,

    onTouchMove: function(e) {
        if (!this.isTracking) {
            return;
        }

        var touches = Array.prototype.slice.call(e.touches),
            firstPoint, secondPoint, distance;

        firstPoint = touches[0].point;
        secondPoint = touches[1].point;

        distance = firstPoint.getDistanceTo(secondPoint);

        if (distance === 0) {
            return;
        }

        if (!this.isStarted) {

            this.isStarted = true;

            this.startDistance = distance;

            this.fire('pinchstart', e, touches, {
                touches: touches,
                distance: distance,
                scale: 1
            });
        }
        else {
            this.fire('pinch', e, touches, {
                touches: touches,
                distance: distance,
                scale: distance / this.startDistance
            });
        }

        this.lastTouches = touches;
    },

    fireEnd: function(e) {
        this.fire('pinchend', e, this.lastTouches);
    },

    fail: function() {
        return this.callParent(arguments);
    }
});

Ext.define('Ext.event.recognizer.Rotate', {
    extend: 'Ext.event.recognizer.MultiTouch',

    requiredTouchesCount: 2,

    handledEvents: ['rotatestart', 'rotate', 'rotateend'],

    startAngle: 0,

    lastTouches: null,

    onTouchMove: function(e) {
        if (!this.isTracking) {
            return;
        }

        var touches = Array.prototype.slice.call(e.touches),
            firstPoint, secondPoint, angle;

        firstPoint = touches[0].point;
        secondPoint = touches[1].point;

        angle = firstPoint.getAngleTo(secondPoint);

        if (!this.isStarted) {
            this.isStarted = true;

            this.startAngle = angle;

            this.fire('rotatestart', e, touches, {
                touches: touches,
                angle: angle,
                rotation: 0
            });
        }
        else {
            this.fire('rotate', e, touches, {
                touches: touches,
                angle: angle,
                rotation: angle - this.startAngle
            });
        }

        this.lastTouches = touches;
    },

    fireEnd: function(e) {
        this.fire('rotateend', e, this.lastTouches);
    }
});

Ext.define('Ext.event.recognizer.SingleTouch', {
    extend: 'Ext.event.recognizer.Touch',

    inheritableStatics: {
        NOT_SINGLE_TOUCH: 0x01,
        TOUCH_MOVED:  0x02
    },

    onTouchStart: function(e) {
        if (e.touches.length > 1) {
            return this.fail(this.self.NOT_SINGLE_TOUCH);
        }
    }
});


Ext.define('Ext.event.recognizer.DoubleTap', {

    extend: 'Ext.event.recognizer.SingleTouch',

    config: {
        maxDuration: 300
    },

    handledEvents: ['singletap', 'doubletap'],

    singleTapTimer: null,

    onTouchStart: function(e) {
        if (this.callParent(arguments) === false) {
            return false;
        }

        this.startTime = e.time;
        clearTimeout(this.singleTapTimer);
    },

    onTouchMove: function() {
        return this.fail(this.self.TOUCH_MOVED);
    },

    onEnd: function(e) {
        var me = this,
            maxDuration = this.getMaxDuration(),
            touch = e.changedTouches[0],
            time = e.time,
            lastTapTime = this.lastTapTime,
            duration;

        this.lastTapTime = time;

        if (lastTapTime) {
            duration = time - lastTapTime;

            if (duration <= maxDuration) {
                this.lastTapTime = 0;

                this.fire('doubletap', e, [touch], {
                    touch: touch,
                    duration: duration
                });

                return;
            }
        }

        if (time - this.startTime > maxDuration) {
            this.fireSingleTap(e, touch);
        }
        else {
            this.singleTapTimer = setTimeout(function() {
                me.fireSingleTap(e, touch);
            }, maxDuration);
        }
    },

    fireSingleTap: function(e, touch) {
        this.fire('singletap', e, [touch], {
            touch: touch
        });
    }
});

Ext.define('Ext.event.recognizer.Drag', {
    extend: 'Ext.event.recognizer.SingleTouch',

    handledEvents: ['dragstart', 'drag', 'dragend'],

    isStarted: false,

    startPoint: null,

    previousPoint: null,

    lastPoint: null,

    onTouchStart: function(e) {
        var startTouches,
            startTouch;

        if (this.callParent(arguments) === false) {
            return false;
        }

        this.startTouches = startTouches = e.changedTouches;
        this.startTouch = startTouch = startTouches[0];
        this.startPoint = startTouch.point;
    },

    onTouchMove: function(e) {
        var touches = e.changedTouches,
            touch = touches[0],
            point = touch.point,
            time = e.time;

        if (this.lastPoint) {
            this.previousPoint = this.lastPoint;
        }

        if (this.lastTime) {
            this.previousTime = this.lastTime;
        }

        this.lastTime = time;
        this.lastPoint = point;

        if (!this.isStarted) {
            this.isStarted = true;

            this.startTime = time;
            this.previousTime = time;

            this.previousPoint = this.startPoint;

            this.fire('dragstart', e, this.startTouches, this.getInfo(e, this.startTouch));
        }
        else {
            this.fire('drag', e, touches, this.getInfo(e, touch));
        }
    },

    onTouchEnd: function(e) {
        if (this.isStarted) {
            var touches = e.changedTouches,
                touch = touches[0],
                point = touch.point;

            this.isStarted = false;

            this.lastPoint = point;

            this.fire('dragend', e, touches, this.getInfo(e, touch));

            this.startTime = 0;
            this.previousTime = 0;
            this.lastTime = 0;

            this.startPoint = null;
            this.previousPoint = null;
            this.lastPoint = null;
        }
    },

    getInfo: function(e, touch) {
        var time = e.time,
            startPoint = this.startPoint,
            previousPoint = this.previousPoint,
            startTime = this.startTime,
            previousTime = this.previousTime,
            point = this.lastPoint,
            deltaX = point.x - startPoint.x,
            deltaY = point.y - startPoint.y,
            info = {
                touch: touch,
                startX: startPoint.x,
                startY: startPoint.y,
                previousX: previousPoint.x,
                previousY: previousPoint.y,
                pageX: point.x,
                pageY: point.y,
                deltaX: deltaX,
                deltaY: deltaY,
                absDeltaX: Math.abs(deltaX),
                absDeltaY: Math.abs(deltaY),
                previousDeltaX: point.x - previousPoint.x,
                previousDeltaY: point.y - previousPoint.y,
                time: time,
                startTime: startTime,
                previousTime: previousTime,
                deltaTime: time - startTime,
                previousDeltaTime: time - previousTime
            };

        return info;
    }
});

Ext.define('Ext.event.recognizer.LongPress', {
    extend: 'Ext.event.recognizer.SingleTouch',

    inheritableStatics: {
        DURATION_NOT_ENOUGH: 0x20
    },

    config: {
        minDuration: 1000
    },

    handledEvents: ['longpress'],

    fireLongPress: function(e) {
        var touch = e.changedTouches[0];

        this.fire('longpress', e, [touch], {
            touch: touch,
            duration: this.getMinDuration()
        });

        this.isLongPress = true;
    },

    onTouchStart: function(e) {
        var me = this;

        if (this.callParent(arguments) === false) {
            return false;
        }

        this.isLongPress = false;

        this.timer = setTimeout(function() {
            me.fireLongPress(e);
        }, this.getMinDuration());
    },

    onTouchMove: function() {
        return this.fail(this.self.TOUCH_MOVED);
    },

    onTouchEnd: function() {
        if (!this.isLongPress) {
            return this.fail(this.self.DURATION_NOT_ENOUGH);
        }
    },

    fail: function() {
        clearTimeout(this.timer);

        return this.callParent(arguments);
    }

}, function() {
    this.override({
        handledEvents: ['longpress', 'taphold'],

        fire: function(eventName) {
            if (eventName === 'longpress') {
                var args = Array.prototype.slice.call(arguments);
                args[0] = 'taphold';

                this.fire.apply(this, args);
            }

            return this.callOverridden(arguments);
        }
    });
});

Ext.define('Ext.event.recognizer.Swipe', {
    extend: 'Ext.event.recognizer.SingleTouch',

    handledEvents: ['swipe'],

    inheritableStatics: {
        MAX_OFFSET_EXCEEDED: 0x10,
        MAX_DURATION_EXCEEDED: 0x11,
        DISTANCE_NOT_ENOUGH: 0x12
    },

    config: {
        minDistance: 80,
        maxOffset: 35,
        maxDuration: 1000
    },

    onTouchStart: function(e) {
        if (this.callParent(arguments) === false) {
            return false;
        }

        var touch = e.changedTouches[0];

        this.startTime = e.time;

        this.isHorizontal = true;
        this.isVertical = true;

        this.startX = touch.pageX;
        this.startY = touch.pageY;
    },

    onTouchMove: function(e) {
        var touch = e.changedTouches[0],
            x = touch.pageX,
            y = touch.pageY,
            absDeltaX = Math.abs(x - this.startX),
            absDeltaY = Math.abs(y - this.startY),
            time = e.time;

        if (time - this.startTime > this.getMaxDuration()) {
            return this.fail(this.self.MAX_DURATION_EXCEEDED);
        }

        if (this.isVertical && absDeltaX > this.getMaxOffset()) {
            this.isVertical = false;
        }

        if (this.isHorizontal && absDeltaY > this.getMaxOffset()) {
            this.isHorizontal = false;
        }

        if (!this.isHorizontal && !this.isVertical) {
            return this.fail(this.self.MAX_OFFSET_EXCEEDED);
        }
    },

    onTouchEnd: function(e) {
        if (this.onTouchMove(e) === false) {
            return false;
        }

        var touch = e.changedTouches[0],
            x = touch.pageX,
            y = touch.pageY,
            deltaX = x - this.startX,
            deltaY = y - this.startY,
            absDeltaX = Math.abs(deltaX),
            absDeltaY = Math.abs(deltaY),
            minDistance = this.getMinDistance(),
            duration = e.time - this.startTime,
            direction, distance;

        if (this.isVertical && absDeltaY < minDistance) {
            this.isVertical = false;
        }

        if (this.isHorizontal && absDeltaX < minDistance) {
            this.isHorizontal = false;
        }

        if (this.isHorizontal) {
            direction = (deltaX < 0) ? 'left' : 'right';
            distance = absDeltaX;
        }
        else if (this.isVertical) {
            direction = (deltaY < 0) ? 'up' : 'down';
            distance = absDeltaY;
        }
        else {
            return this.fail(this.self.DISTANCE_NOT_ENOUGH);
        }

        this.fire('swipe', e, [touch], {
            touch: touch,
            direction: direction,
            distance: distance,
            duration: duration
        });
    }
});

Ext.define('Ext.event.recognizer.HorizontalSwipe', {
    extend: 'Ext.event.recognizer.Swipe',

    handledEvents: ['swipe'],

    onTouchStart: function(e) {
        if (this.callParent(arguments) === false) {
            return false;
        }

        var touch = e.changedTouches[0];

        this.startTime = e.time;

        this.startX = touch.pageX;
        this.startY = touch.pageY;
    },

    onTouchMove: function(e) {
        var touch = e.changedTouches[0],
            y = touch.pageY,
            absDeltaY = Math.abs(y - this.startY),
            time = e.time,
            maxDuration = this.getMaxDuration(),
            maxOffset = this.getMaxOffset();

        if (time - this.startTime > maxDuration) {
            return this.fail(this.self.MAX_DURATION_EXCEEDED);
        }

        if (absDeltaY > maxOffset) {
            return this.fail(this.self.MAX_OFFSET_EXCEEDED);
        }
    },

    onTouchEnd: function(e) {
        if (this.onTouchMove(e) !== false) {
            var touch = e.changedTouches[0],
                x = touch.pageX,
                deltaX = x - this.startX,
                distance = Math.abs(deltaX),
                duration = e.time - this.startTime,
                minDistance = this.getMinDistance(),
                direction;

            if (distance < minDistance) {
                return this.fail(this.self.DISTANCE_NOT_ENOUGH);
            }

            direction = (deltaX < 0) ? 'left' : 'right';

            this.fire('swipe', e, [touch], {
                touch: touch,
                direction: direction,
                distance: distance,
                duration: duration
            });
        }
    }
});

Ext.define('Ext.event.recognizer.Tap', {

    handledEvents: ['tap'],

    extend: 'Ext.event.recognizer.SingleTouch',

    onTouchMove: function() {
        return this.fail(this.self.TOUCH_MOVED);
    },

    onTouchEnd: function(e) {
        var touch = e.changedTouches[0];

        this.fire('tap', e, [touch]);
    }

}, function() {
    this.override({
        handledEvents: ['tap', 'tapstart', 'tapcancel'],

        onTouchStart: function(e) {
            if (this.callOverridden(arguments) === false) {
                return false;
            }

            this.fire('tapstart', e, [e.changedTouches[0]]);
        },

        onTouchMove: function(e) {
            this.fire('tapcancel', e, [e.changedTouches[0]]);

            return this.callOverridden(arguments);
        }
    });
});

Ext.define('Ext.event.recognizer.VerticalSwipe', {
    extend: 'Ext.event.recognizer.Swipe',

    handledEvents: ['swipeup', 'swipedown'],

    onTouchStart: function(e) {
        if (this.callParent(arguments) === false) {
            return false;
        }

        var touch = e.changedTouches[0];

        this.startTime = e.time;

        this.startX = touch.pageX;
        this.startY = touch.pageY;
    },

    onTouchMove: function(e) {
        var touch = e.changedTouches[0],
            x = touch.pageX,
            absDeltaX = Math.abs(x - this.startX),
            maxDuration = this.getMaxDuration(),
            maxOffset = this.getMaxOffset(),
            time = e.time;

        if (time - this.startTime > maxDuration) {
            return this.fail(this.self.MAX_DURATION_EXCEEDED);
        }

        if (absDeltaX > maxOffset) {
            return this.fail(this.self.MAX_OFFSET_EXCEEDED);
        }
    },

    onTouchEnd: function(e) {
        if (this.onTouchMove(e) !== false) {
            var touch = e.changedTouches[0],
                y = touch.pageY,
                deltaY = y - this.startY,
                distance = Math.abs(deltaY),
                duration = e.time - this.startTime,
                minDistance = this.getMinDistance(),
                direction;

            if (distance < minDistance) {
                return this.fail(this.self.DISTANCE_NOT_ENOUGH);
            }

            direction = (deltaY < 0) ? 'up' : 'down';

            this.fire('swipe' + direction, e, [touch], {
                touch: touch,
                distance: distance,
                duration: duration
            });
        }
    }
});





(function() {




function xf(format) {
    var args = Array.prototype.slice.call(arguments, 1);
    return format.replace(/\{(\d+)\}/g, function(m, i) {
        return args[i];
    });
}

Ext.DateExtras = {
    
    now: Date.now || function() {
        return +new Date();
    },

    
    getElapsed: function(dateA, dateB) {
        return Math.abs(dateA - (dateB || new Date()));
    },

    
    useStrict: false,

    
    formatCodeToRegex: function(character, currentGroup) {
        
        var p = utilDate.parseCodes[character];

        if (p) {
          p = typeof p == 'function'? p() : p;
          utilDate.parseCodes[character] = p; 
        }

        return p ? Ext.applyIf({
          c: p.c ? xf(p.c, currentGroup || "{0}") : p.c
        }, p) : {
            g: 0,
            c: null,
            s: Ext.String.escapeRegex(character) 
        };
    },

    
    parseFunctions: {
        "MS": function(input, strict) {
            
            
            var re = new RegExp('\\/Date\\(([-+])?(\\d+)(?:[+-]\\d{4})?\\)\\/');
            var r = (input || '').match(re);
            return r? new Date(((r[1] || '') + r[2]) * 1) : null;
        }
    },
    parseRegexes: [],

    
    formatFunctions: {
        "MS": function() {
            
            return '\\/Date(' + this.getTime() + ')\\/';
        }
    },

    y2kYear : 50,

    
    MILLI : "ms",

    
    SECOND : "s",

    
    MINUTE : "mi",

    
    HOUR : "h",

    
    DAY : "d",

    
    MONTH : "mo",

    
    YEAR : "y",

    
    defaults: {},

    
    dayNames : [
        "Sunday",
        "Monday",
        "Tuesday",
        "Wednesday",
        "Thursday",
        "Friday",
        "Saturday"
    ],

    
    monthNames : [
        "January",
        "February",
        "March",
        "April",
        "May",
        "June",
        "July",
        "August",
        "September",
        "October",
        "November",
        "December"
    ],

    
    monthNumbers : {
        Jan:0,
        Feb:1,
        Mar:2,
        Apr:3,
        May:4,
        Jun:5,
        Jul:6,
        Aug:7,
        Sep:8,
        Oct:9,
        Nov:10,
        Dec:11
    },
    
    defaultFormat : "m/d/Y",
    
    getShortMonthName : function(month) {
        return utilDate.monthNames[month].substring(0, 3);
    },

    
    getShortDayName : function(day) {
        return utilDate.dayNames[day].substring(0, 3);
    },

    
    getMonthNumber : function(name) {
        
        return utilDate.monthNumbers[name.substring(0, 1).toUpperCase() + name.substring(1, 3).toLowerCase()];
    },

    
    formatCodes : {
        d: "Ext.String.leftPad(this.getDate(), 2, '0')",
        D: "Ext.Date.getShortDayName(this.getDay())", 
        j: "this.getDate()",
        l: "Ext.Date.dayNames[this.getDay()]",
        N: "(this.getDay() ? this.getDay() : 7)",
        S: "Ext.Date.getSuffix(this)",
        w: "this.getDay()",
        z: "Ext.Date.getDayOfYear(this)",
        W: "Ext.String.leftPad(Ext.Date.getWeekOfYear(this), 2, '0')",
        F: "Ext.Date.monthNames[this.getMonth()]",
        m: "Ext.String.leftPad(this.getMonth() + 1, 2, '0')",
        M: "Ext.Date.getShortMonthName(this.getMonth())", 
        n: "(this.getMonth() + 1)",
        t: "Ext.Date.getDaysInMonth(this)",
        L: "(Ext.Date.isLeapYear(this) ? 1 : 0)",
        o: "(this.getFullYear() + (Ext.Date.getWeekOfYear(this) == 1 && this.getMonth() > 0 ? +1 : (Ext.Date.getWeekOfYear(this) >= 52 && this.getMonth() < 11 ? -1 : 0)))",
        Y: "Ext.String.leftPad(this.getFullYear(), 4, '0')",
        y: "('' + this.getFullYear()).substring(2, 4)",
        a: "(this.getHours() < 12 ? 'am' : 'pm')",
        A: "(this.getHours() < 12 ? 'AM' : 'PM')",
        g: "((this.getHours() % 12) ? this.getHours() % 12 : 12)",
        G: "this.getHours()",
        h: "Ext.String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0')",
        H: "Ext.String.leftPad(this.getHours(), 2, '0')",
        i: "Ext.String.leftPad(this.getMinutes(), 2, '0')",
        s: "Ext.String.leftPad(this.getSeconds(), 2, '0')",
        u: "Ext.String.leftPad(this.getMilliseconds(), 3, '0')",
        O: "Ext.Date.getGMTOffset(this)",
        P: "Ext.Date.getGMTOffset(this, true)",
        T: "Ext.Date.getTimezone(this)",
        Z: "(this.getTimezoneOffset() * -60)",

        c: function() { 
            for (var c = "Y-m-dTH:i:sP", code = [], i = 0, l = c.length; i < l; ++i) {
                var e = c.charAt(i);
                code.push(e == "T" ? "'T'" : utilDate.getFormatCode(e)); 
            }
            return code.join(" + ");
        },
        

        U: "Math.round(this.getTime() / 1000)"
    },

    
    isValid : function(y, m, d, h, i, s, ms) {
        
        h = h || 0;
        i = i || 0;
        s = s || 0;
        ms = ms || 0;

        
        var dt = utilDate.add(new Date(y < 100 ? 100 : y, m - 1, d, h, i, s, ms), utilDate.YEAR, y < 100 ? y - 100 : 0);

        return y == dt.getFullYear() &&
            m == dt.getMonth() + 1 &&
            d == dt.getDate() &&
            h == dt.getHours() &&
            i == dt.getMinutes() &&
            s == dt.getSeconds() &&
            ms == dt.getMilliseconds();
    },

    
    parse : function(input, format, strict) {
        var p = utilDate.parseFunctions;
        if (p[format] == null) {
            utilDate.createParser(format);
        }
        return p[format](input, Ext.isDefined(strict) ? strict : utilDate.useStrict);
    },

    
    parseDate: function(input, format, strict){
        return utilDate.parse(input, format, strict);
    },


    
    getFormatCode : function(character) {
        var f = utilDate.formatCodes[character];

        if (f) {
          f = typeof f == 'function'? f() : f;
          utilDate.formatCodes[character] = f; 
        }

        
        return f || ("'" + Ext.String.escape(character) + "'");
    },

    
    createFormat : function(format) {
        var code = [],
            special = false,
            ch = '';

        for (var i = 0; i < format.length; ++i) {
            ch = format.charAt(i);
            if (!special && ch == "\\") {
                special = true;
            } else if (special) {
                special = false;
                code.push("'" + Ext.String.escape(ch) + "'");
            } else {
                code.push(utilDate.getFormatCode(ch));
            }
        }
        utilDate.formatFunctions[format] = Ext.functionFactory("return " + code.join('+'));
    },

    
    createParser : (function() {
        var code = [
            "var dt, y, m, d, h, i, s, ms, o, z, zz, u, v,",
                "def = Ext.Date.defaults,",
                "results = String(input).match(Ext.Date.parseRegexes[{0}]);", 

            "if(results){",
                "{1}",

                "if(u != null){", 
                    "v = new Date(u * 1000);", 
                "}else{",
                    
                    
                    
                    "dt = Ext.Date.clearTime(new Date);",

                    
                    "y = Ext.Number.from(y, Ext.Number.from(def.y, dt.getFullYear()));",
                    "m = Ext.Number.from(m, Ext.Number.from(def.m - 1, dt.getMonth()));",
                    "d = Ext.Number.from(d, Ext.Number.from(def.d, dt.getDate()));",

                    
                    "h  = Ext.Number.from(h, Ext.Number.from(def.h, dt.getHours()));",
                    "i  = Ext.Number.from(i, Ext.Number.from(def.i, dt.getMinutes()));",
                    "s  = Ext.Number.from(s, Ext.Number.from(def.s, dt.getSeconds()));",
                    "ms = Ext.Number.from(ms, Ext.Number.from(def.ms, dt.getMilliseconds()));",

                    "if(z >= 0 && y >= 0){",
                        
                        

                        
                        
                        "v = Ext.Date.add(new Date(y < 100 ? 100 : y, 0, 1, h, i, s, ms), Ext.Date.YEAR, y < 100 ? y - 100 : 0);",

                        
                        "v = !strict? v : (strict === true && (z <= 364 || (Ext.Date.isLeapYear(v) && z <= 365))? Ext.Date.add(v, Ext.Date.DAY, z) : null);",
                    "}else if(strict === true && !Ext.Date.isValid(y, m + 1, d, h, i, s, ms)){", 
                        "v = null;", 
                    "}else{",
                        
                        
                        "v = Ext.Date.add(new Date(y < 100 ? 100 : y, m, d, h, i, s, ms), Ext.Date.YEAR, y < 100 ? y - 100 : 0);",
                    "}",
                "}",
            "}",

            "if(v){",
                
                "if(zz != null){",
                    
                    "v = Ext.Date.add(v, Ext.Date.SECOND, -v.getTimezoneOffset() * 60 - zz);",
                "}else if(o){",
                    
                    "v = Ext.Date.add(v, Ext.Date.MINUTE, -v.getTimezoneOffset() + (sn == '+'? -1 : 1) * (hr * 60 + mn));",
                "}",
            "}",

            "return v;"
        ].join('\n');

        return function(format) {
            var regexNum = utilDate.parseRegexes.length,
                currentGroup = 1,
                calc = [],
                regex = [],
                special = false,
                ch = "";

            for (var i = 0; i < format.length; ++i) {
                ch = format.charAt(i);
                if (!special && ch == "\\") {
                    special = true;
                } else if (special) {
                    special = false;
                    regex.push(Ext.String.escape(ch));
                } else {
                    var obj = utilDate.formatCodeToRegex(ch, currentGroup);
                    currentGroup += obj.g;
                    regex.push(obj.s);
                    if (obj.g && obj.c) {
                        calc.push(obj.c);
                    }
                }
            }

            utilDate.parseRegexes[regexNum] = new RegExp("^" + regex.join('') + "$", 'i');
            utilDate.parseFunctions[format] = Ext.functionFactory("input", "strict", xf(code, regexNum, calc.join('')));
        };
    })(),

    
    parseCodes : {
        
        d: {
            g:1,
            c:"d = parseInt(results[{0}], 10);\n",
            s:"(\\d{2})" 
        },
        j: {
            g:1,
            c:"d = parseInt(results[{0}], 10);\n",
            s:"(\\d{1,2})" 
        },
        D: function() {
            for (var a = [], i = 0; i < 7; a.push(utilDate.getShortDayName(i)), ++i); 
            return {
                g:0,
                c:null,
                s:"(?:" + a.join("|") +")"
            };
        },
        l: function() {
            return {
                g:0,
                c:null,
                s:"(?:" + utilDate.dayNames.join("|") + ")"
            };
        },
        N: {
            g:0,
            c:null,
            s:"[1-7]" 
        },
        S: {
            g:0,
            c:null,
            s:"(?:st|nd|rd|th)"
        },
        w: {
            g:0,
            c:null,
            s:"[0-6]" 
        },
        z: {
            g:1,
            c:"z = parseInt(results[{0}], 10);\n",
            s:"(\\d{1,3})" 
        },
        W: {
            g:0,
            c:null,
            s:"(?:\\d{2})" 
        },
        F: function() {
            return {
                g:1,
                c:"m = parseInt(Ext.Date.getMonthNumber(results[{0}]), 10);\n", 
                s:"(" + utilDate.monthNames.join("|") + ")"
            };
        },
        M: function() {
            for (var a = [], i = 0; i < 12; a.push(utilDate.getShortMonthName(i)), ++i); 
            return Ext.applyIf({
                s:"(" + a.join("|") + ")"
            }, utilDate.formatCodeToRegex("F"));
        },
        m: {
            g:1,
            c:"m = parseInt(results[{0}], 10) - 1;\n",
            s:"(\\d{2})" 
        },
        n: {
            g:1,
            c:"m = parseInt(results[{0}], 10) - 1;\n",
            s:"(\\d{1,2})" 
        },
        t: {
            g:0,
            c:null,
            s:"(?:\\d{2})" 
        },
        L: {
            g:0,
            c:null,
            s:"(?:1|0)"
        },
        o: function() {
            return utilDate.formatCodeToRegex("Y");
        },
        Y: {
            g:1,
            c:"y = parseInt(results[{0}], 10);\n",
            s:"(\\d{4})" 
        },
        y: {
            g:1,
            c:"var ty = parseInt(results[{0}], 10);\n"
                + "y = ty > Ext.Date.y2kYear ? 1900 + ty : 2000 + ty;\n", 
            s:"(\\d{1,2})"
        },
        
        a: {
            g:1,
            c:"if (/(am)/i.test(results[{0}])) {\n"
                + "if (!h || h == 12) { h = 0; }\n"
                + "} else { if (!h || h < 12) { h = (h || 0) + 12; }}",
            s:"(am|pm|AM|PM)"
        },
        A: {
            g:1,
            c:"if (/(am)/i.test(results[{0}])) {\n"
                + "if (!h || h == 12) { h = 0; }\n"
                + "} else { if (!h || h < 12) { h = (h || 0) + 12; }}",
            s:"(AM|PM|am|pm)"
        },
        g: function() {
            return utilDate.formatCodeToRegex("G");
        },
        G: {
            g:1,
            c:"h = parseInt(results[{0}], 10);\n",
            s:"(\\d{1,2})" 
        },
        h: function() {
            return utilDate.formatCodeToRegex("H");
        },
        H: {
            g:1,
            c:"h = parseInt(results[{0}], 10);\n",
            s:"(\\d{2})" 
        },
        i: {
            g:1,
            c:"i = parseInt(results[{0}], 10);\n",
            s:"(\\d{2})" 
        },
        s: {
            g:1,
            c:"s = parseInt(results[{0}], 10);\n",
            s:"(\\d{2})" 
        },
        u: {
            g:1,
            c:"ms = results[{0}]; ms = parseInt(ms, 10)/Math.pow(10, ms.length - 3);\n",
            s:"(\\d+)" 
        },
        O: {
            g:1,
            c:[
                "o = results[{0}];",
                "var sn = o.substring(0,1),", 
                    "hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60),", 
                    "mn = o.substring(3,5) % 60;", 
                "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))? (sn + Ext.String.leftPad(hr, 2, '0') + Ext.String.leftPad(mn, 2, '0')) : null;\n" 
            ].join("\n"),
            s: "([+\-]\\d{4})" 
        },
        P: {
            g:1,
            c:[
                "o = results[{0}];",
                "var sn = o.substring(0,1),", 
                    "hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60),", 
                    "mn = o.substring(4,6) % 60;", 
                "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))? (sn + Ext.String.leftPad(hr, 2, '0') + Ext.String.leftPad(mn, 2, '0')) : null;\n" 
            ].join("\n"),
            s: "([+\-]\\d{2}:\\d{2})" 
        },
        T: {
            g:0,
            c:null,
            s:"[A-Z]{1,4}" 
        },
        Z: {
            g:1,
            c:"zz = results[{0}] * 1;\n" 
                  + "zz = (-43200 <= zz && zz <= 50400)? zz : null;\n",
            s:"([+\-]?\\d{1,5})" 
        },
        c: function() {
            var calc = [],
                arr = [
                    utilDate.formatCodeToRegex("Y", 1), 
                    utilDate.formatCodeToRegex("m", 2), 
                    utilDate.formatCodeToRegex("d", 3), 
                    utilDate.formatCodeToRegex("h", 4), 
                    utilDate.formatCodeToRegex("i", 5), 
                    utilDate.formatCodeToRegex("s", 6), 
                    {c:"ms = results[7] || '0'; ms = parseInt(ms, 10)/Math.pow(10, ms.length - 3);\n"}, 
                    {c:[ 
                        "if(results[8]) {", 
                            "if(results[8] == 'Z'){",
                                "zz = 0;", 
                            "}else if (results[8].indexOf(':') > -1){",
                                utilDate.formatCodeToRegex("P", 8).c, 
                            "}else{",
                                utilDate.formatCodeToRegex("O", 8).c, 
                            "}",
                        "}"
                    ].join('\n')}
                ];

            for (var i = 0, l = arr.length; i < l; ++i) {
                calc.push(arr[i].c);
            }

            return {
                g:1,
                c:calc.join(""),
                s:[
                    arr[0].s, 
                    "(?:", "-", arr[1].s, 
                        "(?:", "-", arr[2].s, 
                            "(?:",
                                "(?:T| )?", 
                                arr[3].s, ":", arr[4].s,  
                                "(?::", arr[5].s, ")?", 
                                "(?:(?:\\.|,)(\\d+))?", 
                                "(Z|(?:[-+]\\d{2}(?::)?\\d{2}))?", 
                            ")?",
                        ")?",
                    ")?"
                ].join("")
            };
        },
        U: {
            g:1,
            c:"u = parseInt(results[{0}], 10);\n",
            s:"(-?\\d+)" 
        }
    },

    
    
    dateFormat: function(date, format) {
        return utilDate.format(date, format);
    },

    
    format: function(date, format) {
        if (utilDate.formatFunctions[format] == null) {
            utilDate.createFormat(format);
        }
        var result = utilDate.formatFunctions[format].call(date);
        return result + '';
    },

    
    getTimezone : function(date) {
        
        
        
        
        
        
        
        
        
        
        
        
        return date.toString().replace(/^.* (?:\((.*)\)|([A-Z]{1,4})(?:[\-+][0-9]{4})?(?: -?\d+)?)$/, "$1$2").replace(/[^A-Z]/g, "");
    },

    
    getGMTOffset : function(date, colon) {
        var offset = date.getTimezoneOffset();
        return (offset > 0 ? "-" : "+")
            + Ext.String.leftPad(Math.floor(Math.abs(offset) / 60), 2, "0")
            + (colon ? ":" : "")
            + Ext.String.leftPad(Math.abs(offset % 60), 2, "0");
    },

    
    getDayOfYear: function(date) {
        var num = 0,
            d = Ext.Date.clone(date),
            m = date.getMonth(),
            i;

        for (i = 0, d.setDate(1), d.setMonth(0); i < m; d.setMonth(++i)) {
            num += utilDate.getDaysInMonth(d);
        }
        return num + date.getDate() - 1;
    },

    
    getWeekOfYear : (function() {
        
        var ms1d = 864e5, 
            ms7d = 7 * ms1d; 

        return function(date) { 
            var DC3 = Date.UTC(date.getFullYear(), date.getMonth(), date.getDate() + 3) / ms1d, 
                AWN = Math.floor(DC3 / 7), 
                Wyr = new Date(AWN * ms7d).getUTCFullYear();

            return AWN - Math.floor(Date.UTC(Wyr, 0, 7) / ms7d) + 1;
        };
    })(),

    
    isLeapYear : function(date) {
        var year = date.getFullYear();
        return !!((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
    },

    
    getFirstDayOfMonth : function(date) {
        var day = (date.getDay() - (date.getDate() - 1)) % 7;
        return (day < 0) ? (day + 7) : day;
    },

    
    getLastDayOfMonth : function(date) {
        return utilDate.getLastDateOfMonth(date).getDay();
    },


    
    getFirstDateOfMonth : function(date) {
        return new Date(date.getFullYear(), date.getMonth(), 1);
    },

    
    getLastDateOfMonth : function(date) {
        return new Date(date.getFullYear(), date.getMonth(), utilDate.getDaysInMonth(date));
    },

    
    getDaysInMonth: (function() {
        var daysInMonth = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];

        return function(date) { 
            var m = date.getMonth();

            return m == 1 && utilDate.isLeapYear(date) ? 29 : daysInMonth[m];
        };
    })(),

    
    getSuffix : function(date) {
        switch (date.getDate()) {
            case 1:
            case 21:
            case 31:
                return "st";
            case 2:
            case 22:
                return "nd";
            case 3:
            case 23:
                return "rd";
            default:
                return "th";
        }
    },

    
    clone : function(date) {
        return new Date(date.getTime());
    },

    
    isDST : function(date) {
        
        
        return new Date(date.getFullYear(), 0, 1).getTimezoneOffset() != date.getTimezoneOffset();
    },

    
    clearTime : function(date, clone) {
        if (clone) {
            return Ext.Date.clearTime(Ext.Date.clone(date));
        }

        
        var d = date.getDate();

        
        date.setHours(0);
        date.setMinutes(0);
        date.setSeconds(0);
        date.setMilliseconds(0);

        if (date.getDate() != d) { 
            
            

            
            for (var hr = 1, c = utilDate.add(date, Ext.Date.HOUR, hr); c.getDate() != d; hr++, c = utilDate.add(date, Ext.Date.HOUR, hr));

            date.setDate(d);
            date.setHours(c.getHours());
        }

        return date;
    },

    
    add : function(date, interval, value) {
        var d = Ext.Date.clone(date),
            Date = Ext.Date;
        if (!interval || value === 0) return d;

        switch(interval.toLowerCase()) {
            case Ext.Date.MILLI:
                d.setMilliseconds(d.getMilliseconds() + value);
                break;
            case Ext.Date.SECOND:
                d.setSeconds(d.getSeconds() + value);
                break;
            case Ext.Date.MINUTE:
                d.setMinutes(d.getMinutes() + value);
                break;
            case Ext.Date.HOUR:
                d.setHours(d.getHours() + value);
                break;
            case Ext.Date.DAY:
                d.setDate(d.getDate() + value);
                break;
            case Ext.Date.MONTH:
                var day = date.getDate();
                if (day > 28) {
                    day = Math.min(day, Ext.Date.getLastDateOfMonth(Ext.Date.add(Ext.Date.getFirstDateOfMonth(date), 'mo', value)).getDate());
                }
                d.setDate(day);
                d.setMonth(date.getMonth() + value);
                break;
            case Ext.Date.YEAR:
                d.setFullYear(date.getFullYear() + value);
                break;
        }
        return d;
    },

    
    between : function(date, start, end) {
        var t = date.getTime();
        return start.getTime() <= t && t <= end.getTime();
    }
};

var utilDate = Ext.DateExtras;

Ext.apply(Ext.Date, utilDate);

Ext.apply(Ext.util.Date, utilDate);

})();




Ext.define('Ext.fx.Animation', {

    requires: [
        'Ext.fx.animation.Slide',
        'Ext.fx.animation.Fade',
        'Ext.fx.animation.Flip',
        'Ext.fx.animation.Pop',
        'Ext.fx.animation.Cube'
    ],

    constructor: function(config) {
        var defaultClass = Ext.fx.animation.Abstract,
            type;

        if (typeof config == 'string') {
            type = config;
            config = {};
        }
        else if (config.type) {
            type = config.type;
        }

        if (type) {
            defaultClass = Ext.ClassManager.getByAlias('animation.' + type);

            if (!defaultClass) {
                Ext.Logger.error("Invalid animation type of: '" + type + "'");
            }
        }

        return Ext.factory(config, defaultClass);
    }
});


Ext.define('Ext.fx.animation.Wipe', {
    extend: 'Ext.fx.Animation',
    alternateClassName: 'Ext.fx.animation.WipeIn',

    config: {
        
        easing: 'ease-out',

        
        direction: 'right',

        
        out: false
    },

    refresh: function() {
        var me = this,
            el        = me.getElement(),
            elBox     = el.dom.getBoundingClientRect(),
            elWidth   = elBox.width,
            elHeight  = elBox.height,
            from      = me.getFrom(),
            to        = me.getTo(),
            out       = me.getOut(),
            direction = me.getDirection(),
            maskFromX = 0,
            maskFromY = 0,
            maskToX   = 0,
            maskToY   = 0,
            mask, tmp;

        switch (direction) {
            case 'up':
                if (out) {
                    mask = '-webkit-gradient(linear, left top, left bottom, from(#000), to(transparent), color-stop(33%, #000), color-stop(66%, transparent))';
                    maskFromY = elHeight * 3 + 'px';
                    maskToY = elHeight + 'px';
                } else {
                    mask = '-webkit-gradient(linear, left top, left bottom, from(transparent), to(#000), color-stop(66%, #000), color-stop(33%, transparent))';
                    maskFromY = -elHeight * 2 + 'px';
                    maskToY = 0;
                }

                break;

            case 'down':
                if (out) {
                    mask = '-webkit-gradient(linear, left top, left bottom, from(transparent), to(#000), color-stop(66%, #000), color-stop(33%, transparent))';
                    maskFromY = -elHeight * 2 + 'px';
                    maskToY = 0;
                } else {
                    mask = '-webkit-gradient(linear, left top, left bottom, from(#000), to(transparent), color-stop(33%, #000), color-stop(66%, transparent))';
                    maskFromY = elHeight * 3 + 'px';
                    maskToY = elHeight + 'px';
                }

                break;

            case 'right':
                if (out) {
                    mask = '-webkit-gradient(linear, right top, left top, from(#000), to(transparent), color-stop(33%, #000), color-stop(66%, transparent))';
                    maskFromX = -elWidth * 2 + 'px';
                    maskToX = 0;
                } else {
                    mask = '-webkit-gradient(linear, right top, left top, from(transparent), to(#000), color-stop(66%, #000), color-stop(33%, transparent))';
                    maskToX = -elWidth * 2 + 'px';
                }

                break;

            case 'left':
                if (out) {
                    mask = '-webkit-gradient(linear, right top, left top, from(transparent), to(#000), color-stop(66%, #000), color-stop(33%, transparent))';
                    maskToX = -elWidth * 2 + 'px';
                } else {
                    mask = '-webkit-gradient(linear, right top, left top, from(#000), to(transparent), color-stop(33%, #000), color-stop(66%, transparent))';
                    maskFromX = -elWidth * 2 + 'px';
                    maskToX = 0;
                }

                break;
        }

        if (!out) {
            tmp = maskFromY;
            maskFromY = maskToY;
            maskToY = tmp;

            tmp = maskFromX;
            maskFromX = maskToX;
            maskToX = tmp;
        }

        from.set('mask-image', mask);
        from.set('mask-size', elWidth * 3 + 'px ' + elHeight * 3 + 'px');
        from.set('mask-position-x', maskFromX);
        from.set('mask-position-y', maskFromY);

        to.set('mask-position-x', maskToX);
        to.set('mask-position-y', maskToY);

        
    },

    getData: function() {
        this.refresh();

        return this.callParent(arguments);
    }
});


Ext.define('Ext.fx.animation.WipeOut', {
    extend: 'Ext.fx.animation.Wipe',

    config: {
        
        out: true
    }
});

Ext.define('Ext.fx.layout.card.Style', {

    extend: 'Ext.fx.layout.card.Abstract',

    requires: [
        'Ext.fx.Animation'
    ],

    config: {
        inAnimation: {
            before: {
                visibility: ''
            },
            preserveEndState: false
        },

        outAnimation: {
            preserveEndState: false
        }
    },

    constructor: function(config) {
        var animationConfig = {},
            name, inAnimation, outAnimation;

        this.initConfig(config);

        inAnimation = this.getInAnimation();
        outAnimation = this.getOutAnimation();

        for (name in config) {
            if (config.hasOwnProperty(name)) {
                if (!this.hasConfig(name)) {
                    animationConfig[name] = config[name];
                }
            }
        }

        inAnimation.setConfig(animationConfig);
        outAnimation.setConfig(animationConfig);
    },

    applyInAnimation: function(animation, inAnimation) {
        return Ext.factory(animation, Ext.fx.Animation, inAnimation);
    },

    applyOutAnimation: function(animation, outAnimation) {
        return Ext.factory(animation, Ext.fx.Animation, outAnimation);
    },

    updateInAnimation: function(animation) {
        animation.setScope(this);
    },

    updateOutAnimation: function(animation) {
        animation.setScope(this);
    },

    onActiveItemChange: function(newItem, oldItem) {
        var inAnimation = this.getInAnimation(),
            outAnimation = this.getOutAnimation(),
            inElement, outElement,
            previousInElement, previousOutElement;

        if (newItem && oldItem) {
            inElement = newItem.renderElement;
            outElement = oldItem.renderElement;

            previousInElement = inAnimation.getElement();
            inAnimation.setElement(inElement);

            previousOutElement = outAnimation.getElement();
            outAnimation.setElement(outElement);
            outAnimation.setOnBeforeEnd(function(element, isInterrupted) {
                if (!isInterrupted) {
                    oldItem.hide();
                }
            });

            inElement.dom.style.visibility = 'hidden !important';
            newItem.show();

            Ext.Animator.run([outAnimation, inAnimation]);
            return false;
        }
    }
});


Ext.define('Ext.fx.layout.card.Cube', {
    extend: 'Ext.fx.layout.card.Style',

    alias: 'fx.layout.card.cube',

    config: {
        reverse: null,
        inAnimation: {
            type: 'cube',
            out: true
        },
        outAnimation: {
            type: 'cube'
        }
    }
});


Ext.define('Ext.fx.layout.card.Fade', {
    extend: 'Ext.fx.layout.card.Style',

    alias: 'fx.layout.card.fade',

    config: {
        reverse: null,
        
        inAnimation: {
            type: 'fade',
            easing: 'ease-out'
        },
        outAnimation: {
            type: 'fade',
            easing: 'ease-out',
            out: true
        }
    }
});


Ext.define('Ext.fx.layout.card.Flip', {
    extend: 'Ext.fx.layout.card.Style',

    alias: 'fx.layout.card.flip',

    config: {
        duration: 500,

        inAnimation: {
            type: 'flip',
            half: true,
            easing: 'ease-out'
        },
        outAnimation: {
            type: 'flip',
            half: true,
            easing: 'ease-in',
            out: true
        }
    },

    updateDuration: function(duration) {
        var halfDuration = duration / 2,
            inAnimation = this.getInAnimation(),
            outAnimation = this.getOutAnimation();

        inAnimation.setDelay(halfDuration);
        inAnimation.setDuration(halfDuration);
        outAnimation.setDuration(halfDuration);
    }
});


Ext.define('Ext.fx.layout.card.Pop', {
    extend: 'Ext.fx.layout.card.Style',

    alias: 'fx.layout.card.pop',

    config: {
        duration: 500,
        reverse: null,

        inAnimation: {
            type: 'pop',
            easing: 'ease-out'
        },
        outAnimation: {
            type: 'pop',
            easing: 'ease-in',
            out: true
        }
    },

    updateDuration: function(duration) {
        var halfDuration = duration / 2,
            inAnimation = this.getInAnimation(),
            outAnimation = this.getOutAnimation();

        inAnimation.setDelay(halfDuration);
        inAnimation.setDuration(halfDuration);
        outAnimation.setDuration(halfDuration);
    }
});


Ext.define('Ext.fx.layout.card.Slide', {
    extend: 'Ext.fx.layout.card.Style',

    alias: 'fx.layout.card.slide',

    config: {
        reverse: null,

        inAnimation: {
            type: 'slide',
            easing: 'ease-out'
        },
        outAnimation: {
            type: 'slide',
            easing: 'ease-out',
            out: true
        }
    },

    updateReverse: function(reverse) {
        this.getInAnimation().setReverse(reverse);
        this.getOutAnimation().setReverse(reverse);
    }
});

Ext.define('Ext.log.formatter.Default', {
    extend: 'Ext.log.formatter.Formatter',

    config: {
        messageFormat: "[{priorityName}][{callerDisplayName}] {message}"
    },

    format: function(event) {
        var event = Ext.merge({}, event, {
                priorityName: event.priorityName.toUpperCase()
            });

        return this.callParent([event]);
    }
});

Ext.define('Ext.log.formatter.Identity', {
    extend: 'Ext.log.formatter.Default',

    config: {
        messageFormat: "[{osIdentity}][{browserIdentity}][{timestamp}][{priorityName}][{callerDisplayName}] {message}"
    },

    format: function(event) {
        event.timestamp = Ext.Date.toString();
        event.browserIdentity = Ext.browser.name + ' ' + Ext.browser.version;
        event.osIdentity = Ext.os.name + ' ' + Ext.os.version;

        return this.callParent(arguments);
    }
});

Ext.define('Ext.log.writer.Console', {

    extend: 'Ext.log.writer.Writer',

    config: {
        throwOnErrors: true
    },

    doWrite: function(event) {
        var message = event.message,
            priority = event.priorityName,
            consoleMethod;

        if (priority === 'error' && this.getThrowOnErrors()) {
            throw new Error(message);
        }

        if (typeof console !== 'undefined') {
            consoleMethod = priority;

            if (consoleMethod === 'deprecate') {
                consoleMethod = 'warn';
            }

            if (!(consoleMethod in console)) {
                consoleMethod = 'log';
            }

            console[consoleMethod](message);
        }
    }
});

Ext.define('Ext.log.writer.DocumentTitle', {

    extend: 'Ext.log.writer.Writer',

    doWrite: function(event) {
        var message = event.message;

        document.title = message;
    }
});

(function() {


var Observable = Ext.define('Ext.mixin.Observable', {

    requires: ['Ext.event.Dispatcher'],

    extend: 'Ext.mixin.Mixin',

    mixins: ['Ext.mixin.Identifiable'],

    mixinConfig: {
        id: 'observable',
        beforeHooks: {
            constructor: 'constructor'
        },
        hooks: {
            destroy: 'destroy'
        }
    },

    alternateClassName: 'Ext.util.Observable',

    statics: {
        releaseCapture: function(o) {
            console.log('TODO: static releaseCapture');
        },

        capture: function(o, fn, scope) {
            console.log('TODO: static capture');
        },

        observe: function(cls, listeners) {
            console.log('TODO: static observe');
        }
    },

    
    isObservable: true,

    observableType: 'observable',

    validIdRegex: /^([\w\-]+)$/,

    observableIdPrefix: '#',

    ADD_LISTENER_ACTION: 'doAddListener',

    REMOVE_LISTENER_ACTION: 'doRemoveListener',

    listenerOptionsRegex: /^(?:delay|buffer|single|args|delegate)$/,

    config: {
        
        listeners: null,

        
        bubbleEvents: null
    },

    constructor: function(config) {
        if (config) {
            if ('listeners' in config) {
                this.setListeners(config.listeners);
                delete config.listeners;
            }

            if ('bubbleEvents' in config) {
                this.setBubbleEvents(config.bubbleEvents);
                delete config.bubbleEvents;
            }
        }

        return this;
    },

    applyListeners: function(listeners) {
        if (listeners) {
            this.addListener(listeners);
        }
    },

    applyBubbleEvents: function(bubbleEvents) {
        if (bubbleEvents) {
            this.enableBubble(bubbleEvents);
        }
    },

    getOptimizedObservableId: function() {
        return this.observableId;
    },

    getObservableId: function() {
        if (!this.observableId) {
            var id = this.getUniqueId();

            if (!id.match(this.validIdRegex)) {
                Ext.Logger.error("Invalid unique id of '" + id + "' for this object", this);
            }

            this.observableId = this.observableIdPrefix + id;

            this.getObservableId = this.getOptimizedObservableId;
        }

        return this.observableId;
    },

    getOptimizedEventDispatcher: function() {
        return this.eventDispatcher;
    },

    getEventDispatcher: function() {
        if (!this.eventDispatcher) {
            this.eventDispatcher = Ext.event.Dispatcher.getInstance();

            this.getEventDispatcher = this.getOptimizedEventDispatcher;
        }

        return this.eventDispatcher;
    },

    getManagedListeners: function(object, eventName) {
        var id = object.getUniqueId(),
            managedListeners = this.managedListeners;

        if (!managedListeners) {
            this.managedListeners = managedListeners = {};
        }

        if (!managedListeners[id]) {
            managedListeners[id] = {};
            object.doAddListener('destroy', 'clearManagedListeners', this, {
                single: true,
                args: [object]
            });
        }

        if (!managedListeners[id][eventName]) {
            managedListeners[id][eventName] = [];
        }

        return managedListeners[id][eventName];
    },

    getUsedSelectors: function() {
        var selectors = this.usedSelectors;

        if (!selectors) {
            selectors = this.usedSelectors = [];
            selectors.$map = {};
        }

        return selectors;
    },

    
    fireEvent: function(eventName) {
        var args = Array.prototype.slice.call(arguments, 1);

        return this.doFireEvent(eventName, args);
    },

    fireAction: function(eventName, args, fn, scope, options, order) {
        var actions = [];

        if (args === undefined) {
            args = [];
        }

        if (fn !== undefined) {
            actions.push({
                fn: fn,
                scope: scope || this,
                options: options,
                order: order
            });
        }

        return this.doFireEvent(eventName, args, actions);
    },

    doFireEvent: function(eventName, args, actions) {
        if (this.eventFiringSuspended) {
            return;
        }

        var id = this.getObservableId(),
            dispatcher = this.getEventDispatcher();

        return dispatcher.dispatchEvent(this.observableType, id, eventName, args, actions);
    },

    
    doAddListener: function(name, fn, scope, options, order) {
        if (typeof fn !== 'string' && typeof fn !== 'function') {
             scope = fn.scope || scope;

             if (fn.before) {
                 this.doAddListener(name, fn.before, scope, options, 'before');
             }

             if (fn.after) {
                 this.doAddListener(name, fn.after, scope, options, 'current');
             }

             return;
        }

        var isManaged = (scope && scope !== this && scope.isIdentifiable),
            dispatcher = this.getEventDispatcher(),
            usedSelectors = this.getUsedSelectors(),
            usedSelectorsMap = usedSelectors.$map,
            selector = this.getObservableId(),
            isAdded, managedListeners, delegate;

        if (!options) {
            options = {};
        }

        if (!scope) {
            scope = this;
        }

        if (options.delegate) {
            delegate = options.delegate;
            
            selector += ' ' + delegate;
        }

        if (!(selector in usedSelectorsMap)) {
            usedSelectorsMap[selector] = true;
            usedSelectors.push(selector);
        }

        isAdded = dispatcher.addListener(this.observableType, selector, name, fn, scope, options, order);

        if (isAdded && isManaged) {
            managedListeners = this.getManagedListeners(scope, name);
            managedListeners.push({
                delegate: delegate,
                scope: scope,
                fn: fn,
                order: order
            });
        }

        return isAdded;
    },

    doRemoveListener: function(name, fn, scope, options, order) {
        if (typeof fn !== 'string' && typeof fn !== 'function') {
             scope = fn.scope || scope;

             if (fn.before) {
                 this.doRemoveListener(name, fn.before, scope, options, 'before');
             }

             if (fn.after) {
                 this.doRemoveListener(name, fn.after, scope, options, 'current');
             }

             return;
        }

        var isManaged = (scope && scope !== this && scope.isIdentifiable),
            selector = this.getObservableId(),
            isRemoved,
            managedListeners, i, ln, listener, delegate;

        if (options && options.delegate) {
            delegate = options.delegate;
            
            selector += ' ' + delegate;
        }

        if (!scope) {
            scope = this;
        }

        isRemoved = this.getEventDispatcher().removeListener(this.observableType, selector, name, fn, scope, order);

        if (isRemoved && isManaged) {
            managedListeners = this.getManagedListeners(scope, name);

            for (i = 0,ln = managedListeners.length; i < ln; i++) {
                listener = managedListeners[i];

                if (listener.fn === fn && listener.scope === scope && listener.delegate === delegate && listener.order === order) {
                    managedListeners.splice(i, 1);
                    break;
                }
            }
        }

        return isRemoved;
    },

    clearManagedListeners: function(object) {
        var managedListeners = this.managedListeners,
            id, namedListeners, listeners, eventName, i, ln, listener, options;

        if (!managedListeners) {
            return this;
        }

        if (object) {
            if (typeof object != 'string') {
                id = object.getUniqueId();
            }
            else {
                id = object;
            }

            namedListeners = managedListeners[id];

            for (eventName in namedListeners) {
                if (namedListeners.hasOwnProperty(eventName)) {
                    listeners = namedListeners[eventName];

                    for (i = 0,ln = listeners.length; i < ln; i++) {
                        listener = listeners[i];

                        options = {};

                        if (listener.delegate) {
                            options.delegate = listener.delegate;
                        }

                        if (this.doRemoveListener(eventName, listener.fn, listener.scope, options, listener.order)) {
                            i--;
                            ln--;
                        }
                    }
                }
            }

            delete managedListeners[id];
            return this;
        }

        for (id in managedListeners) {
            if (managedListeners.hasOwnProperty(id)) {
                this.clearManagedListeners(id);
            }
        }
    },

    
    changeListener: function(action, eventName, fn, scope, options, order) {
        var eventNames,
            listeners,
            listenerOptionsRegex,
            actualOptions,
            name, value, i, ln, listener;

        if (typeof fn != 'undefined') {
            
            if (typeof eventName != 'string') {
                for (i = 0,ln = eventName.length; i < ln; i++) {
                    name = eventName[i];

                    this[action](name, fn, scope, options, order);
                }

                return this;
            }

            this[action](eventName, fn, scope, options, order);
        }
        else if (Ext.isArray(eventName)) {
            listeners = eventName;

            for (i = 0,ln = listeners.length; i < ln; i++) {
                listener = listeners[i];

                this[action](listener.event, listener.fn, listener.scope, listener, listener.order);
            }
        }
        else {
            listenerOptionsRegex = this.listenerOptionsRegex;
            options = eventName;
            eventNames = [];
            listeners = [];
            actualOptions = {};

            for (name in options) {
                if (options.hasOwnProperty(name)) {
                    value = options[name];

                    if (name === 'scope') {
                        scope = value;
                        continue;
                    }
                    else if (name === 'order') {
                        order = value;
                        continue;
                    }

                    if (!listenerOptionsRegex.test(name)) {
                        eventNames.push(name);
                        listeners.push(value);
                    }
                    else {
                        actualOptions[name] = value;
                    }
                }
            }

            for (i = 0,ln = eventNames.length; i < ln; i++) {
                this[action](eventNames[i], listeners[i], scope, actualOptions, order);
            }
        }

    },

    
    addListener: function(eventName, fn, scope, options, order) {
        return this.changeListener(this.ADD_LISTENER_ACTION, eventName, fn, scope, options, order);
    },

    addBeforeListener: function(eventName, fn, scope, options) {
        return this.addListener(eventName, fn, scope, options, 'before');
    },

    addAfterListener: function(eventName, fn, scope, options) {
        return this.addListener(eventName, fn, scope, options, 'after');
    },

    
    removeListener: function(eventName, fn, scope, options, order) {
        return this.changeListener(this.REMOVE_LISTENER_ACTION, eventName, fn, scope, options, order);
    },

    removeBeforeListener: function(eventName, fn, scope, options) {
        return this.removeListener(eventName, fn, scope, options, 'before');
    },

    removeAfterListener: function(eventName, fn, scope, options) {
        return this.removeListener(eventName, fn, scope, options, 'after');
    },

    
    clearListeners: function() {
        var usedSelectors = this.getUsedSelectors(),
            dispatcher = this.getEventDispatcher(),
            i, ln, selector;

        for (i = 0,ln = usedSelectors.length; i < ln; i++) {
            selector = usedSelectors[i];

            dispatcher.clearListeners(this.observableType, selector);
        }
    },

    
    hasListener: function(eventName) {
        return this.getEventDispatcher().hasListener(this.observableType, this.getObservableId(), eventName);
    },

    
    suspendEvents: function(queueSuspended) {
        this.eventFiringSuspended = true;
    },

    
    resumeEvents: function() {
        this.eventFiringSuspended = false;
    },

    
    relayEvents: function(object, events, prefix) {
        var i, ln, oldName, newName;

        if (typeof prefix == 'undefined') {
            prefix = '';
        }

        if (typeof events == 'string') {
            events = [events];
        }
        if (Ext.isArray(events)) {
            for (i = 0,ln = events.length; i < ln; i++) {
                oldName = events[i];
                newName = prefix + oldName;

                object.addListener(oldName, this.createEventRelayer(newName), this);
            }
        }
        else {
            for (oldName in events) {
                if (events.hasOwnProperty(oldName)) {
                    newName = prefix + events[oldName];

                    object.addListener(oldName, this.createEventRelayer(newName), this);
                }
            }
        }

        return this;
    },

    
    createEventRelayer: function(newName){
        return function() {
            return this.doFireEvent(newName, Array.prototype.slice.call(arguments, 0, -2));
        }
    },

    
    enableBubble: function(events) {
        var isBubblingEnabled = this.isBubblingEnabled,
            i, ln, name;

        if (!isBubblingEnabled) {
            isBubblingEnabled = this.isBubblingEnabled = {};
        }

        if (typeof events == 'string') {
            events = Ext.Array.clone(arguments);
        }

        for (i = 0,ln = events.length; i < ln; i++) {
            name = events[i];

            if (!isBubblingEnabled[name]) {
                isBubblingEnabled[name] = true;
                this.addListener(name, this.createEventBubbler(name), this);
            }
        }
    },

    createEventBubbler: function(name) {
        return function doBubbleEvent() {
            var bubbleTarget = ('getBubbleTarget' in this) ? this.getBubbleTarget() : null;

            if (bubbleTarget && bubbleTarget !== this && bubbleTarget.isObservable) {
                bubbleTarget.fireAction(name, Array.prototype.slice.call(arguments, 0, -2), doBubbleEvent, bubbleTarget, null, 'after');
            }
        }
    },

    getBubbleTarget: function() {
        return false;
    },

    destroy: function() {
        if (this.observableId) {
            this.fireEvent('destroy');
            this.clearListeners();
            this.clearManagedListeners();
        }
    },

    addEvents: Ext.emptyFn

}, function() {
    this.createAlias({
        on: 'addListener',
        un: 'removeListener',
        onBefore: 'addBeforeListener',
        onAfter: 'addAfterListener',
        unBefore: 'addBeforeListener',
        unAfter: 'addAfterListener'
    });

    Ext.deprecateClassMethod(this, 'addEvents', function(){}, "addEvents() is deprecated. It's no longer needed to add events before firing");

    Ext.deprecateClassMethod(this, 'addManagedListener', function(object, eventName, fn, scope, options) {
        return object.addListener(eventName, fn, scope, options);
    }, "addManagedListener() / mon() is deprecated, simply use addListener() / on(). All listeners are now automatically managed where necessary.");

    Ext.deprecateClassMethod(this, 'removeManagedListener', function(object, eventName, fn, scope) {
        return object.removeListener(eventName, fn, scope);
    }, "removeManagedListener() / mun() is deprecated, simply use removeListener() / un(). All listeners are now automatically managed where necessary.");

    this.createAlias({
        mon: 'addManagedListener',
        mun: 'removeManagedListener'
    });
});

})();


Ext.define('Ext.data.Batch', {
    mixins: {
        observable: 'Ext.util.Observable'
    },

    
    autoStart: false,

    
    current: -1,

    
    total: 0,

    
    isRunning: false,

    
    isComplete: false,

    
    hasException: false,

    
    pauseOnException: true,

    
    constructor: function(config) {
        var me = this;

        

        

        

        me.mixins.observable.constructor.call(me, config);

        
        me.operations = [];
    },

    
    add: function(operation) {
        this.total++;

        operation.setBatch(this);

        this.operations.push(operation);
    },

    
    start: function() {
        this.hasException = false;
        this.isRunning = true;

        this.runNextOperation();
    },

    
    runNextOperation: function() {
        this.runOperation(this.current + 1);
    },

    
    pause: function() {
        this.isRunning = false;
    },

    
    runOperation: function(index) {
        var me = this,
            operations = me.operations,
            operation = operations[index],
            onProxyReturn;

        if (operation === undefined) {
            me.isRunning = false;
            me.isComplete = true;
            me.fireEvent('complete', me, operations[operations.length - 1]);
        } else {
            me.current = index;

            onProxyReturn = function(operation) {
                var hasException = operation.hasException();

                if (hasException) {
                    me.hasException = true;
                    me.fireEvent('exception', me, operation);
                } else {
                    me.fireEvent('operationcomplete', me, operation);
                }

                if (hasException && me.pauseOnException) {
                    me.pause();
                } else {
                    operation.setCompleted();
                    me.runNextOperation();
                }
            };

            operation.setStarted();

            me.proxy[operation.action](operation, onProxyReturn, me);
        }
    }
});

Ext.define('Ext.data.Connection', {
    mixins: {
        observable: 'Ext.util.Observable'
    },

    statics: {
        requestId: 0
    },

    url: null,
    async: true,
    method: null,
    username: '',
    password: '',

    
    disableCaching: true,

    
    disableCachingParam: '_dc',

    
    timeout : 30000,

    

    useDefaultHeader : true,
    defaultPostHeader : 'application/x-www-form-urlencoded; charset=UTF-8',
    useDefaultXhrHeader : true,
    defaultXhrHeader : 'XMLHttpRequest',

    constructor : function(config) {
        config = config || {};
        Ext.apply(this, config);

        
        
        
        this.requests = {};
        this.mixins.observable.constructor.call(this);
    },

    
    request : function(options) {
        options = options || {};
        var me = this,
            scope = options.scope || window,
            username = options.username || me.username,
            password = options.password || me.password || '',
            async,
            requestOptions,
            request,
            headers,
            xhr;

        if (me.fireEvent('beforerequest', me, options) !== false) {

            requestOptions = me.setOptions(options, scope);

            if (this.isFormUpload(options) === true) {
                this.upload(options.form, requestOptions.url, requestOptions.data, options);
                return null;
            }

            
            if (options.autoAbort === true || me.autoAbort) {
                me.abort();
            }

            
            xhr = this.getXhrInstance();

            async = options.async !== false ? (options.async || me.async) : false;

            
            if (username) {
                xhr.open(requestOptions.method, requestOptions.url, async, username, password);
            } else {
                xhr.open(requestOptions.method, requestOptions.url, async);
            }

            headers = me.setupHeaders(xhr, options, requestOptions.data, requestOptions.params);

            
            request = {
                id: ++Ext.data.Connection.requestId,
                xhr: xhr,
                headers: headers,
                options: options,
                async: async,
                timeout: setTimeout(function() {
                    request.timedout = true;
                    me.abort(request);
                }, options.timeout || me.timeout)
            };
            me.requests[request.id] = request;

            
            if (async) {
                xhr.onreadystatechange = Ext.Function.bind(me.onStateChange, me, [request]);
            }

            
            xhr.send(requestOptions.data);
            if (!async) {
                return this.onComplete(request);
            }
            return request;
        } else {
            Ext.callback(options.callback, options.scope, [options, undefined, undefined]);
            return null;
        }
    },

    
    upload: function(form, url, params, options) {
        form = Ext.getDom(form);
        options = options || {};

        var id = Ext.id(),
            frame = document.createElement('iframe'),
            hiddens = [],
            encoding = 'multipart/form-data',
            buf = {
                target: form.target,
                method: form.method,
                encoding: form.encoding,
                enctype: form.enctype,
                action: form.action
            }, addField = function(name, value) {
            hiddenItem = document.createElement('input');
            Ext.fly(hiddenItem).set({
                type: 'hidden',
                value: value,
                name: name
            });
            form.appendChild(hiddenItem);
            hiddens.push(hiddenItem);
        }, hiddenItem;

        
        Ext.fly(frame).set({
            id: id,
            name: id,
            cls: Ext.baseCSSPrefix + 'hide-display',
            src: Ext.SSL_SECURE_URL
        });

        document.body.appendChild(frame);

        
        if (document.frames) {
            document.frames[id].name = id;
        }

        Ext.fly(form).set({
            target: id,
            method: 'POST',
            enctype: encoding,
            encoding: encoding,
            action: url || buf.action
        });

        
        if (params) {
            Ext.iterate(Ext.Object.fromQueryString(params), function(name, value) {
                if (Ext.isArray(value)) {
                    Ext.each(value, function(v) {
                        addField(name, v);
                    });
                } else {
                    addField(name, value);
                }
            });
        }

        Ext.fly(frame).on('load', Ext.Function.bind(this.onUploadComplete, this, [frame, options]), null, {single: true});
        form.submit();

        Ext.fly(form).set(buf);
        Ext.each(hiddens, function(h) {
            Ext.removeNode(h);
        });
    },

    onUploadComplete: function(frame, options) {
        var me = this,
            
            response = {
                responseText: '',
                responseXML: null
            }, doc, firstChild;

        try {
            doc = frame.contentWindow.document || frame.contentDocument || window.frames[id].document;
            if (doc) {
                if (doc.body) {
                    if (/textarea/i.test((firstChild = doc.body.firstChild || {}).tagName)) { 
                        response.responseText = firstChild.value;
                    } else {
                        response.responseText = doc.body.innerHTML;
                    }
                }
                
                response.responseXML = doc.XMLDocument || doc;
            }
        } catch (e) {
        }

        me.fireEvent('requestcomplete', me, response, options);

        Ext.callback(options.success, options.scope, [response, options]);
        Ext.callback(options.callback, options.scope, [options, true, response]);

        setTimeout(function() {
            Ext.removeNode(frame);
        }, 100);
    },

    
    isFormUpload: function(options) {
        var form = this.getForm(options);
        if (form) {
            return (options.isUpload || (/multipart\/form-data/i).test(form.getAttribute('enctype')));
        }
        return false;
    },

    
    getForm: function(options) {
        return Ext.getDom(options.form) || null;
    },

    
    setOptions: function(options, scope) {
        var me = this,
            params = options.params || {},
            extraParams = me.extraParams,
            urlParams = options.urlParams,
            url = options.url || me.url,
            jsonData = options.jsonData,
            method,
            disableCache,
            data;


        
        if (Ext.isFunction(params)) {
            params = params.call(scope, options);
        }

        
        if (Ext.isFunction(url)) {
            url = url.call(scope, options);
        }

        url = this.setupUrl(options, url);

        if (!url) {
            Ext.Error.raise({
                options: options,
                msg: 'No URL specified'
            });
        }

        
        data = options.rawData || options.xmlData || jsonData || null;
        if (jsonData && !Ext.isPrimitive(jsonData)) {
            data = Ext.encode(data);
        }

        
        if (Ext.isObject(params)) {
            params = Ext.Object.toQueryString(params);
        }

        if (Ext.isObject(extraParams)) {
            extraParams = Ext.Object.toQueryString(extraParams);
        }

        params = params + ((extraParams) ? ((params) ? '&' : '') + extraParams : '');

        urlParams = Ext.isObject(urlParams) ? Ext.Object.toQueryString(urlParams) : urlParams;

        params = this.setupParams(options, params);

        
        method = (options.method || me.method || ((params || data) ? 'POST' : 'GET')).toUpperCase();
        this.setupMethod(options, method);


        disableCache = options.disableCaching !== false ? (options.disableCaching || me.disableCaching) : false;
        
        if (method === 'GET' && disableCache) {
            url = Ext.urlAppend(url, (options.disableCachingParam || me.disableCachingParam) + '=' + (new Date().getTime()));
        }

        
        if ((method == 'GET' || data) && params) {
            url = Ext.urlAppend(url, params);
            params = null;
        }

        
        if (urlParams) {
            url = Ext.urlAppend(url, urlParams);
        }

        return {
            url: url,
            method: method,
            data: data || params || null
        };
    },

    
    setupUrl: function(options, url) {
        var form = this.getForm(options);
        if (form) {
            url = url || form.action;
        }
        return url;
    },


    
    setupParams: function(options, params) {
        var form = this.getForm(options),
            serializedForm;
        if (form && !this.isFormUpload(options)) {
            serializedForm = Ext.Element.serializeForm(form);
            params = params ? (params + '&' + serializedForm) : serializedForm;
        }
        return params;
    },

    
    setupMethod: function(options, method) {
        if (this.isFormUpload(options)) {
            return 'POST';
        }
        return method;
    },

    
    setupHeaders: function(xhr, options, data, params) {
        var me = this,
            headers = Ext.apply({}, options.headers || {}, me.defaultHeaders || {}),
            contentType = me.defaultPostHeader,
            jsonData = options.jsonData,
            xmlData = options.xmlData,
            key,
            header;

        if (!headers['Content-Type'] && (data || params)) {
            if (data) {
                if (options.rawData) {
                    contentType = 'text/plain';
                } else {
                    if (xmlData && Ext.isDefined(xmlData)) {
                        contentType = 'text/xml';
                    } else if (jsonData && Ext.isDefined(jsonData)) {
                        contentType = 'application/json';
                    }
                }
            }
            headers['Content-Type'] = contentType;
        }

        if (me.useDefaultXhrHeader && !headers['X-Requested-With']) {
            headers['X-Requested-With'] = me.defaultXhrHeader;
        }
        
        try {
            for (key in headers) {
                if (headers.hasOwnProperty(key)) {
                    header = headers[key];
                    xhr.setRequestHeader(key, header);
                }

            }
        } catch(e) {
            me.fireEvent('exception', key, header);
        }
        return headers;
    },

    
    getXhrInstance: (function() {
        var options = [function() {
            return new XMLHttpRequest();
        }, function() {
            return new ActiveXObject('MSXML2.XMLHTTP.3.0');
        }, function() {
            return new ActiveXObject('MSXML2.XMLHTTP');
        }, function() {
            return new ActiveXObject('Microsoft.XMLHTTP');
        }], i = 0,
            len = options.length,
            xhr;

        for (; i < len; ++i) {
            try {
                xhr = options[i];
                xhr();
                break;
            } catch(e) {
            }
        }
        return xhr;
    })(),

    
    isLoading : function(request) {
        if (!(request && request.xhr)) {
            return false;
        }
        
        var state = request.xhr.readyState;
        return !(state === 0 || state == 4);
    },

    
    abort : function(request) {
        var me = this,
            requests = me.requests,
            id;

        if (request && me.isLoading(request)) {
            
            request.xhr.onreadystatechange = null;
            request.xhr.abort();
            me.clearTimeout(request);
            if (!request.timedout) {
                request.aborted = true;
            }
            me.onComplete(request);
            me.cleanup(request);
        } else if (!request) {
            for (id in requests) {
                if (requests.hasOwnProperty(id)) {
                    me.abort(requests[id]);
                }
            }
        }
    },

    
    onStateChange : function(request) {
        if (request.xhr.readyState == 4) {
            this.clearTimeout(request);
            this.onComplete(request);
            this.cleanup(request);
        }
    },

    
    clearTimeout: function(request) {
        clearTimeout(request.timeout);
        delete request.timeout;
    },

    
    cleanup: function(request) {
        request.xhr = null;
        delete request.xhr;
    },

    
    onComplete : function(request) {
        var me = this,
            options = request.options,
            result,
            success,
            response;

        try {
            result = me.parseStatus(request.xhr.status);
        } catch (e) {
            
            result = {
                success : false,
                isException : false
            };
        }
        success = result.success;

        if (success) {
            response = me.createResponse(request);
            me.fireEvent('requestcomplete', me, response, options);
            Ext.callback(options.success, options.scope, [response, options]);
        } else {
            if (result.isException || request.aborted || request.timedout) {
                response = me.createException(request);
            } else {
                response = me.createResponse(request);
            }
            me.fireEvent('requestexception', me, response, options);
            Ext.callback(options.failure, options.scope, [response, options]);
        }
        Ext.callback(options.callback, options.scope, [options, success, response]);
        delete me.requests[request.id];
        return response;
    },

    
    parseStatus: function(status) {
        
        status = status == 1223 ? 204 : status;

        var success = (status >= 200 && status < 300) || status == 304,
            isException = false;

        if (!success) {
            switch (status) {
                case 12002:
                case 12029:
                case 12030:
                case 12031:
                case 12152:
                case 13030:
                    isException = true;
                    break;
            }
        }
        return {
            success: success,
            isException: isException
        };
    },

    
    createResponse : function(request) {
        var xhr = request.xhr,
            headers = {},
            lines = xhr.getAllResponseHeaders().replace(/\r\n/g, '\n').split('\n'),
            count = lines.length,
            line, index, key, value, response;

        while (count--) {
            line = lines[count];
            index = line.indexOf(':');
            if (index >= 0) {
                key = line.substr(0, index).toLowerCase();
                if (line.charAt(index + 1) == ' ') {
                    ++index;
                }
                headers[key] = line.substr(index + 1);
            }
        }

        request.xhr = null;
        delete request.xhr;

        response = {
            request: request,
            requestId : request.id,
            status : xhr.status,
            statusText : xhr.statusText,
            getResponseHeader : function(header) {
                return headers[header.toLowerCase()];
            },
            getAllResponseHeaders : function() {
                return headers;
            },
            responseText : xhr.responseText,
            responseXML : xhr.responseXML
        };

        
        
        xhr = null;
        return response;
    },

    
    createException : function(request) {
        return {
            request : request,
            requestId : request.id,
            status : request.aborted ? -1 : 0,
            statusText : request.aborted ? 'transaction aborted' : 'communication failure',
            aborted: request.aborted,
            timedout: request.timedout
        };
    }
});


Ext.define('Ext.Ajax', {
    extend: 'Ext.data.Connection',
    singleton: true,

    
    
    
    
    
    

    

    
    
    
    
    
    

    
    autoAbort : false
});

Ext.define('Ext.data.Tree', {
    alias: 'data.tree',

    mixins: {
        observable: "Ext.util.Observable"
    },

    
    root: null,

    
    constructor: function(root) {
        var me = this;

        me.nodeHash = {};

        me.mixins.observable.constructor.call(me);

        if (root) {
            me.setRootNode(root);
        }
    },

    
    getRootNode : function() {
        return this.root;
    },

    
    setRootNode : function(node) {
        var me = this;

        me.root = node;
        Ext.data.NodeInterface.decorate(node);

        if (me.fireEvent('beforeappend', null, node) !== false) {
            node.set('root', true);
            node.updateInfo();

            me.relayEvents(node, [
                
                "append",

                
                "remove",

                
                "move",

                
                "insert",

                
                "beforeappend",

                
                "beforeremove",

                
                "beforemove",

                
                "beforeinsert",

                 
                 "expand",

                 
                 "collapse",

                 
                 "beforeexpand",

                 
                 "beforecollapse" ,

                 
                 "rootchange"
            ]);

            node.on({
                scope: me,
                insert: me.onNodeInsert,
                append: me.onNodeAppend,
                remove: me.onNodeRemove
            });

            me.registerNode(node);
            me.fireEvent('append', null, node);
            me.fireEvent('rootchange', node);
        }

        return node;
    },

    
    flatten: function(){
        var nodes = [],
            hash = this.nodeHash,
            key;

        for (key in hash) {
            if (hash.hasOwnProperty(key)) {
                nodes.push(hash[key]);
            }
        }
        return nodes;
    },

    
    onNodeInsert: function(parent, node) {
        this.registerNode(node);
    },

    
    onNodeAppend: function(parent, node) {
        this.registerNode(node);
    },

    
    onNodeRemove: function(parent, node) {
        this.unregisterNode(node);
    },

    
    getNodeById : function(id) {
        return this.nodeHash[id];
    },

    
    registerNode : function(node) {
        this.nodeHash[node.getId() || node.internalId] = node;
    },

    
    unregisterNode : function(node) {
        delete this.nodeHash[node.getId() || node.internalId];
    },

    
    sort: function(sorterFn, recursive) {
        this.getRootNode().sort(sorterFn, recursive);
    },

     
    filter: function(filters, recursive) {
        this.getRootNode().filter(filters, recursive);
    }
});

Ext.define('Ext.data.proxy.Proxy', {
    alias: 'proxy.proxy',
    alternateClassName: ['Ext.data.DataProxy', 'Ext.data.Proxy'],
    requires: [
        'Ext.data.reader.Json',
        'Ext.data.writer.Json'
    ],
    uses: [
        'Ext.data.Batch',
        'Ext.data.Operation',
        'Ext.data.Model'
    ],
    mixins: {
        observable: 'Ext.util.Observable'
    },

    
    batchOrder: 'create,update,destroy',

    
    batchActions: true,

    
    defaultReaderType: 'json',

    
    defaultWriterType: 'json',

    

    

    

    isProxy: true,

    
    constructor: function(config) {
        config = config || {};

        if (config.model === undefined) {
            delete config.model;
        }

        Ext.apply(this, config);

        this.mixins.observable.constructor.call(this);

        if (this.model !== undefined && !(this.model instanceof Ext.data.Model)) {
            this.setModel(this.model);
        }

        
    },

    
    setModel: function(model, setOnStore) {
        this.model = Ext.ModelManager.getModel(model);

        var reader = this.reader,
            writer = this.writer;

        this.setReader(reader);
        this.setWriter(writer);

        if (setOnStore && this.store) {
            this.store.setModel(this.model);
        }
    },

    
    getModel: function() {
        return this.model;
    },

    
    setReader: function(reader) {
        var me = this;

        if (reader === undefined || typeof reader == 'string') {
            reader = {
                type: reader
            };
        }

        if (reader.isReader) {
            reader.setModel(me.model);
        } else {
            Ext.applyIf(reader, {
                proxy: me,
                model: me.model,
                type : me.defaultReaderType
            });

            reader = Ext.createByAlias('reader.' + reader.type, reader);
        }

        if (reader.onMetaChange) {
            reader.onMetaChange = Ext.Function.createSequence(reader.onMetaChange, this.onMetaChange, this);
        }

        me.reader = reader;
        return me.reader;
    },

    
    getReader: function() {
        return this.reader;
    },

    onMetaChange: function(meta) {
        this.fireEvent('metachange', this, meta);
    },

    
    setWriter: function(writer) {
        if (writer === undefined || typeof writer == 'string') {
            writer = {
                type: writer
            };
        }

        if (!(writer instanceof Ext.data.writer.Writer)) {
            Ext.applyIf(writer, {
                model: this.model,
                type : this.defaultWriterType
            });

            writer = Ext.createByAlias('writer.' + writer.type, writer);
        }

        this.writer = writer;

        return this.writer;
    },

    
    getWriter: function() {
        return this.writer;
    },

    
    create: Ext.emptyFn,

    
    read: Ext.emptyFn,

    
    update: Ext.emptyFn,

    
    destroy: Ext.emptyFn,

    
    batch: function(operations, listeners) {
        var me = this,
            batch = Ext.create('Ext.data.Batch', {
                proxy: me,
                listeners: listeners || {}
            }),
            useBatch = me.batchActions,
            records;

        Ext.each(me.batchOrder.split(','), function(action) {
            records = operations[action];
            if (records) {
                if (useBatch) {
                    batch.add(Ext.create('Ext.data.Operation', {
                        action: action,
                        records: records
                    }));
                } else {
                    Ext.each(records, function(record) {
                        batch.add(Ext.create('Ext.data.Operation', {
                            action : action,
                            records: [record]
                        }));
                    });
                }
            }
        }, me);

        batch.start();
        return batch;
    }
}, function() {
    

    
    Ext.data.DataProxy = this;
    
    
    
});


Ext.define('Ext.data.proxy.Client', {
    extend: 'Ext.data.proxy.Proxy',
    alternateClassName: 'Ext.data.ClientProxy',
    
    
    clear: function() {
        Ext.Error.raise("The Ext.data.proxy.Client subclass that you are using has not defined a 'clear' function. See src/data/ClientProxy.js for details.");
    }
});

Ext.define('Ext.data.proxy.Memory', {
    extend: 'Ext.data.proxy.Client',
    alias: 'proxy.memory',
    alternateClassName: 'Ext.data.MemoryProxy',

    

    constructor: function(config) {
        this.callParent([config]);

        
        this.setReader(this.reader);
    },

    
    read: function(operation, callback, scope) {
        var me     = this,
            reader = me.getReader(),
            result = reader.read(me.data);

        Ext.apply(operation, {
            resultSet: result
        });

        operation.setCompleted();
        operation.setSuccessful();
        Ext.callback(callback, scope || me, [operation]);
    },

    clear: Ext.emptyFn
});


Ext.define('Ext.data.proxy.Server', {
    extend: 'Ext.data.proxy.Proxy',
    alias : 'proxy.server',
    alternateClassName: 'Ext.data.ServerProxy',
    uses  : ['Ext.data.Request'],

    

    
    pageParam: 'page',

    
    startParam: 'start',

    
    limitParam: 'limit',

    
    groupParam: 'group',

    
    sortParam: 'sort',

    
    filterParam: 'filter',

    
    directionParam: 'dir',

    
    simpleSortMode: false,

    
    noCache : true,

    
    cacheString: "_dc",

    
    timeout : 30000,

    

    constructor: function(config) {
        var me = this;

        config = config || {};
        
        me.callParent([config]);

        
        me.extraParams = config.extraParams || {};

        me.api = config.api || {};

        
        me.nocache = me.noCache;
    },

    
    create: function() {
        return this.doRequest.apply(this, arguments);
    },

    read: function() {
        return this.doRequest.apply(this, arguments);
    },

    update: function() {
        return this.doRequest.apply(this, arguments);
    },

    destroy: function() {
        return this.doRequest.apply(this, arguments);
    },

    
    setExtraParam: function(name, value) {
        this.extraParams[name] = value;
    },

    
    buildRequest: function(operation) {
        var params = Ext.applyIf(operation.params || {}, this.extraParams || {}),
            request;

        
        params = Ext.applyIf(params, this.getParams(operation));

        if (operation.id && !params.id) {
            params.id = operation.id;
        }

        request = Ext.create('Ext.data.Request', {
            params   : params,
            action   : operation.action,
            records  : operation.records,
            operation: operation,
            url      : operation.url
        });

        request.url = this.buildUrl(request);

        
        operation.request = request;

        return request;
    },

    
    processResponse: function(success, operation, request, response, callback, scope) {
        var me = this,
            reader,
            result;

        if (success === true) {
            reader = me.getReader();
            result = reader.read(me.extractResponseData(response));

            if (result.success !== false) {
                
                Ext.apply(operation, {
                    response: response,
                    resultSet: result
                });

                operation.commitRecords(result.records);
                operation.setCompleted();
                operation.setSuccessful();
            } else {
                operation.setException(result.message);
                me.fireEvent('exception', this, response, operation);
            }
        } else {
            me.setException(operation, response);
            me.fireEvent('exception', this, response, operation);
        }

        
        if (typeof callback == 'function') {
            callback.call(scope || me, operation);
        }

        me.afterRequest(request, success);
    },

    
    setException: function(operation, response) {
        operation.setException({
            status: response.status,
            statusText: response.statusText
        });
    },

    
    extractResponseData: function(response) {
        return response;
    },

    
    applyEncoding: function(value) {
        return Ext.encode(value);
    },

    
    encodeSorters: function(sorters) {
        var min = [],
            length = sorters.length,
            i = 0;

        for (; i < length; i++) {
            min[i] = {
                property : sorters[i].property,
                direction: sorters[i].direction
            };
        }
        return this.applyEncoding(min);

    },

    
    encodeFilters: function(filters) {
        var min = [],
            length = filters.length,
            i = 0;

        for (; i < length; i++) {
            min[i] = {
                property: filters[i].property,
                value   : filters[i].value
            };
        }
        return this.applyEncoding(min);
    },

    
    getParams: function(operation) {
        var me = this,
            params = {},
            isDef = Ext.isDefined,
            groupers = operation.groupers,
            sorters = operation.sorters,
            filters = operation.filters,
            page = operation.page,
            start = operation.start,
            limit = operation.limit,

            simpleSortMode = me.simpleSortMode,

            pageParam = me.pageParam,
            startParam = me.startParam,
            limitParam = me.limitParam,
            groupParam = me.groupParam,
            sortParam = me.sortParam,
            filterParam = me.filterParam,
            directionParam = me.directionParam;

        if (pageParam && isDef(page)) {
            params[pageParam] = page;
        }

        if (startParam && isDef(start)) {
            params[startParam] = start;
        }

        if (limitParam && isDef(limit)) {
            params[limitParam] = limit;
        }

        if (groupParam && groupers && groupers.length > 0) {
            
            params[groupParam] = me.encodeSorters(groupers);
        }

        if (sortParam && sorters && sorters.length > 0) {
            if (simpleSortMode) {
                params[sortParam] = sorters[0].property;
                params[directionParam] = sorters[0].direction;
            } else {
                params[sortParam] = me.encodeSorters(sorters);
            }

        }

        if (filterParam && filters && filters.length > 0) {
            params[filterParam] = me.encodeFilters(filters);
        }

        return params;
    },

    
    buildUrl: function(request) {
        var me = this,
            url = me.getUrl(request);

        if (!url) {
            Ext.Error.raise("You are using a ServerProxy but have not supplied it with a url.");
        }

        if (me.noCache) {
            url = Ext.urlAppend(url, Ext.String.format("{0}={1}", me.cacheString, Ext.Date.now()));
        }

        return url;
    },

    
    getUrl: function(request) {
        return request.url || this.api[request.action] || this.url;
    },

    
    doRequest: function(operation, callback, scope) {
        Ext.Error.raise("The doRequest function has not been implemented on your Ext.data.proxy.Server subclass. See src/data/ServerProxy.js for details");
    },

    
    afterRequest: Ext.emptyFn,

    onDestroy: function() {
        Ext.destroy(this.reader, this.writer);
    }
});


Ext.define('Ext.data.proxy.JsonP', {
    extend: 'Ext.data.proxy.Server',
    alternateClassName: 'Ext.data.ScriptTagProxy',
    alias: ['proxy.jsonp', 'proxy.scripttag'],
    requires: ['Ext.data.JsonP'],

    defaultWriterType: 'base',

    
    callbackKey : 'callback',

    
    recordParam: 'records',

    
    autoAppendParams: true,

    constructor: function(){
        this.addEvents(
            
            'exception'
        );
        this.callParent(arguments);
    },

    
    doRequest: function(operation, callback, scope) {
        
        var me      = this,
            writer  = me.getWriter(),
            request = me.buildRequest(operation),
            params = request.params;

        if (operation.allowWrite()) {
            request = writer.write(request);
        }

        
        Ext.apply(request, {
            callbackKey: me.callbackKey,
            timeout: me.timeout,
            scope: me,
            disableCaching: false, 
            callback: me.createRequestCallback(request, operation, callback, scope)
        });

        
        if (me.autoAppendParams) {
            request.params = {};
        }

        request.jsonp = Ext.data.JsonP.request(request);
        
        request.params = params;
        operation.setStarted();
        me.lastRequest = request;

        return request;
    },

    
    createRequestCallback: function(request, operation, callback, scope) {
        var me = this;

        return function(success, response, errorType) {
            delete me.lastRequest;
            me.processResponse(success, operation, request, response, callback, scope);
        };
    },

    
    setException: function(operation, response) {
        operation.setException(operation.request.jsonp.errorType);
    },


    
    buildUrl: function(request) {
        var me      = this,
            url     = me.callParent(arguments),
            params  = Ext.apply({}, request.params),
            filters = params.filters,
            records,
            filter, i;

        delete params.filters;

        if (me.autoAppendParams) {
            url = Ext.urlAppend(url, Ext.Object.toQueryString(params));
        }

        if (filters && filters.length) {
            for (i = 0; i < filters.length; i++) {
                filter = filters[i];

                if (filter.value) {
                    url = Ext.urlAppend(url, filter.property + "=" + filter.value);
                }
            }
        }

        
        records = request.records;

        if (Ext.isArray(records) && records.length > 0) {
            url = Ext.urlAppend(url, Ext.String.format("{0}={1}", me.recordParam, me.encodeRecords(records)));
        }

        return url;
    },

    
    destroy: function() {
        this.abort();
        this.callParent();
    },

    
    abort: function() {
        var lastRequest = this.lastRequest;
        if (lastRequest) {
            Ext.data.JsonP.abort(lastRequest.jsonp);
        }
    },

    
    encodeRecords: function(records) {
        var encoded = "",
            i = 0,
            len = records.length;

        for (; i < len; i++) {
            encoded += Ext.Object.toQueryString(records[i].data);
        }

        return encoded;
    }
});


Ext.define('Ext.data.proxy.WebStorage', {
    extend: 'Ext.data.proxy.Client',
    alternateClassName: 'Ext.data.WebStorageProxy',
    
    
    id: undefined,

    
    constructor: function(config) {
        this.callParent(arguments);
        
        
        this.cache = {};

        if (this.getStorageObject() === undefined) {
            Ext.Error.raise("Local Storage is not supported in this browser, please use another type of data proxy");
        }

        
        this.id = this.id || (this.store ? this.store.storeId : undefined);

        if (this.id === undefined) {
            Ext.Error.raise("No unique id was provided to the local storage proxy. See Ext.data.proxy.LocalStorage documentation for details");
        }

        this.initialize();
    },

    
    create: function(operation, callback, scope) {
        var records = operation.records,
            length  = records.length,
            ids     = this.getIds(),
            id, record, i;
        
        operation.setStarted();

        for (i = 0; i < length; i++) {
            record = records[i];

            if (record.phantom) {
                record.phantom = false;
                id = this.getNextId();
            } else {
                id = record.getId();
            }

            this.setRecord(record, id);
            ids.push(id);
        }

        this.setIds(ids);

        operation.setCompleted();
        operation.setSuccessful();

        if (typeof callback == 'function') {
            callback.call(scope || this, operation);
        }
    },

    
    read: function(operation, callback, scope) {
        

        var records = [],
            ids     = this.getIds(),
            length  = ids.length,
            i, recordData, record;
        
        
        if (operation.id) {
            record = this.getRecord(operation.id);
            
            if (record) {
                records.push(record);
                operation.setSuccessful();
            }
        } else {
            for (i = 0; i < length; i++) {
                records.push(this.getRecord(ids[i]));
            }
            operation.setSuccessful();
        }
        
        operation.setCompleted();

        operation.resultSet = Ext.create('Ext.data.ResultSet', {
            records: records,
            total  : records.length,
            loaded : true
        });

        if (typeof callback == 'function') {
            callback.call(scope || this, operation);
        }
    },

    
    update: function(operation, callback, scope) {
        var records = operation.records,
            length  = records.length,
            ids     = this.getIds(),
            record, id, i;

        operation.setStarted();

        for (i = 0; i < length; i++) {
            record = records[i];
            this.setRecord(record);
            
            
            
            id = record.getId();
            if (id !== undefined && Ext.Array.indexOf(ids, id) == -1) {
                ids.push(id);
            }
        }
        this.setIds(ids);

        operation.setCompleted();
        operation.setSuccessful();

        if (typeof callback == 'function') {
            callback.call(scope || this, operation);
        }
    },

    
    destroy: function(operation, callback, scope) {
        var records = operation.records,
            length  = records.length,
            ids     = this.getIds(),

            
            newIds  = [].concat(ids),
            i;

        for (i = 0; i < length; i++) {
            Ext.Array.remove(newIds, records[i].getId());
            this.removeRecord(records[i], false);
        }

        this.setIds(newIds);
        
        operation.setCompleted();
        operation.setSuccessful();

        if (typeof callback == 'function') {
            callback.call(scope || this, operation);
        }
    },

    
    getRecord: function(id) {
        if (this.cache[id] === undefined) {
            var rawData = Ext.decode(this.getStorageObject().getItem(this.getRecordKey(id))),
                data    = {},
                Model   = this.model,
                fields  = Model.prototype.fields.items,
                length  = fields.length,
                i, field, name, record;

            if (!rawData) {
                return;
            }

            for (i = 0; i < length; i++) {
                field = fields[i];
                name  = field.name;

                if (typeof field.decode == 'function') {
                    data[name] = field.decode(rawData[name]);
                } else {
                    data[name] = rawData[name];
                }
            }

            record = new Model(data, id);
            record.phantom = false;

            this.cache[id] = record;
        }
        
        return this.cache[id];
    },

    
    setRecord: function(record, id) {
        if (id) {
            record.setId(id);
        } else {
            id = record.getId();
        }

        var me = this,
            rawData = record.data,
            data    = {},
            model   = me.model,
            fields  = model.prototype.fields.items,
            length  = fields.length,
            i = 0,
            field, name, obj, key;

        for (; i < length; i++) {
            field = fields[i];
            name  = field.name;

            if (typeof field.encode == 'function') {
                data[name] = field.encode(rawData[name], record);
            } else {
                data[name] = rawData[name];
            }
        }

        obj = me.getStorageObject();
        key = me.getRecordKey(id);
        
        
        me.cache[id] = record;
        
        
        obj.removeItem(key);
        obj.setItem(key, Ext.encode(data));
    },

    
    removeRecord: function(id, updateIds) {
        var me = this,
            ids;
            
        if (id.isModel) {
            id = id.getId();
        }

        if (updateIds !== false) {
            ids = me.getIds();
            Ext.Array.remove(ids, id);
            me.setIds(ids);
        }

        me.getStorageObject().removeItem(me.getRecordKey(id));
    },

    
    getRecordKey: function(id) {
        if (id.isModel) {
            id = id.getId();
        }

        return Ext.String.format("{0}-{1}", this.id, id);
    },

    
    getRecordCounterKey: function() {
        return Ext.String.format("{0}-counter", this.id);
    },

    
    getIds: function() {
        var ids    = (this.getStorageObject().getItem(this.id) || "").split(","),
            length = ids.length,
            i;

        if (length == 1 && ids[0] === "") {
            ids = [];
        }

        return ids;
    },

    
    setIds: function(ids) {
        var obj = this.getStorageObject(),
            str = ids.join(",");
        
        obj.removeItem(this.id);
        
        if (!Ext.isEmpty(str)) {
            obj.setItem(this.id, str);
        }
    },

    
    getNextId: function() {
        var obj  = this.getStorageObject(),
            key  = this.getRecordCounterKey(),
            last = obj.getItem(key),
            ids, id;
        
        if (last === null) {
            ids = this.getIds();
            last = ids[ids.length - 1] || 0;
        }
        
        id = parseInt(last, 10) + 1;
        obj.setItem(key, id);
        
        return id;
    },

    
    initialize: function() {
        var storageObject = this.getStorageObject();
        storageObject.setItem(this.id, storageObject.getItem(this.id) || "");
    },

    
    clear: function() {
        var obj = this.getStorageObject(),
            ids = this.getIds(),
            len = ids.length,
            i;

        
        for (i = 0; i < len; i++) {
            this.removeRecord(ids[i]);
        }

        
        obj.removeItem(this.getRecordCounterKey());
        obj.removeItem(this.id);
    },

    
    getStorageObject: function() {
        Ext.Error.raise("The getStorageObject function has not been defined in your Ext.data.proxy.WebStorage subclass");
    }
});

Ext.define('Ext.data.proxy.LocalStorage', {
    extend: 'Ext.data.proxy.WebStorage',
    alias: 'proxy.localstorage',
    alternateClassName: 'Ext.data.LocalStorageProxy',
    
    
    getStorageObject: function() {
        return window.localStorage;
    }
});

Ext.define('Ext.data.proxy.SessionStorage', {
    extend: 'Ext.data.proxy.WebStorage',
    alias: 'proxy.sessionstorage',
    alternateClassName: 'Ext.data.SessionStorageProxy',
    
    
    getStorageObject: function() {
        return window.sessionStorage;
    }
});


Ext.define('Ext.direct.Provider', {
    
    
   
   alias: 'direct.provider',
   
    mixins: {
        observable: 'Ext.util.Observable'   
    },
   
    
   
   
    
    constructor : function(config){
        var me = this;
        
        Ext.apply(me, config);
        me.addEvents(
                        
            'connect',
                        
            'disconnect',
                        
            'data',
                                    
            'exception'
        );
        me.mixins.observable.constructor.call(me, config);
    },
    
    
    isConnected: function(){
        return false;
    },

    
    connect: Ext.emptyFn,
    
    
    disconnect: Ext.emptyFn
});



Ext.define('Ext.direct.JsonProvider', {

    

    extend: 'Ext.direct.Provider',

    alias: 'direct.jsonprovider',

    uses: ['Ext.direct.ExceptionEvent'],

    

   
   parseResponse: function(response){
        if (!Ext.isEmpty(response.responseText)) {
            if (Ext.isObject(response.responseText)) {
                return response.responseText;
            }
            return Ext.decode(response.responseText);
        }
        return null;
    },

    
    createEvents: function(response){
        var data = null,
            events = [],
            event,
            i = 0,
            len;

        try{
            data = this.parseResponse(response);
        } catch(e) {
            event = Ext.create('Ext.direct.ExceptionEvent', {
                data: e,
                xhr: response,
                code: Ext.direct.Manager.self.exceptions.PARSE,
                message: 'Error parsing json response: \n\n ' + data
            });
            return [event];
        }

        if (Ext.isArray(data)) {
            for (len = data.length; i < len; ++i) {
                events.push(this.createEvent(data[i]));
            }
        } else {
            events.push(this.createEvent(data));
        }
        return events;
    },

    
    createEvent: function(response){
        return Ext.create('direct.' + response.type, response);
    }
});

Ext.define('Ext.direct.PollingProvider', {
    
    
    
    extend: 'Ext.direct.JsonProvider',
    
    alias: 'direct.pollingprovider',
    
    uses: ['Ext.direct.ExceptionEvent'],
    
    requires: ['Ext.Ajax', 'Ext.util.DelayedTask'],
    
    
    
    
    interval: 3000,

    
    
    

    
    constructor : function(config){
        this.callParent(arguments);
        this.addEvents(
            
            'beforepoll',            
            
            'poll'
        );
    },

    
    isConnected: function(){
        return !!this.pollTask;
    },

    
    connect: function(){
        var me = this, url = me.url;
        
        if (url && !me.pollTask) {
            me.pollTask = Ext.TaskManager.start({
                run: function(){
                    if (me.fireEvent('beforepoll', me) !== false) {
                        if (Ext.isFunction(url)) {
                            url(me.baseParams);
                        } else {
                            Ext.Ajax.request({
                                url: url,
                                callback: me.onData,
                                scope: me,
                                params: me.baseParams
                            });
                        }
                    }
                },
                interval: me.interval,
                scope: me
            });
            me.fireEvent('connect', me);
        } else if (!url) {
            Ext.Error.raise('Error initializing PollingProvider, no url configured.');
        }
    },

    
    disconnect: function(){
        var me = this;
        
        if (me.pollTask) {
            Ext.TaskManager.stop(me.pollTask);
            delete me.pollTask;
            me.fireEvent('disconnect', me);
        }
    },

    
    onData: function(opt, success, response){
        var me = this, 
            i = 0, 
            len,
            events;
        
        if (success) {
            events = me.createEvents(response);
            for (len = events.length; i < len; ++i) {
                me.fireEvent('data', me, events[i]);
            }
        } else {
            me.fireEvent('data', me, Ext.create('Ext.direct.ExceptionEvent', {
                data: null,
                code: Ext.direct.Manager.self.exceptions.TRANSPORT,
                message: 'Unable to connect to the server.',
                xhr: response
            }));
        }
    }
});

Ext.define('Ext.util.AbstractMixedCollection', {
    requires: ['Ext.util.Filter'],

    mixins: {
        observable: 'Ext.util.Observable'
    },

    
    
    
    

    constructor: function(allowFunctions, keyFn) {
        var me = this;

        me.items = [];
        me.map = {};
        me.keys = [];
        me.length = 0;

        me.allowFunctions = allowFunctions === true;

        if (keyFn) {
            me.getKey = keyFn;
        }

        me.mixins.observable.constructor.call(me);
    },

    
    allowFunctions : false,

    
    add: function(key, obj){
        var me = this,
            myObj = obj,
            myKey = key,
            old;

        if (arguments.length == 1) {
            myObj = myKey;
            myKey = me.getKey(myObj);
        }
        if (typeof myKey != 'undefined' && myKey !== null) {
            old = me.map[myKey];
            if (typeof old != 'undefined') {
                return me.replace(myKey, myObj);
            }
            me.map[myKey] = myObj;
        }
        me.length++;
        me.items.push(myObj);
        me.keys.push(myKey);
        me.fireEvent('add', me.length - 1, myObj, myKey);
        return myObj;
    },

    
    getKey: function(o){
         return o.id;
    },

    
    replace: function(key, o){
        var me = this,
            old,
            index;

        if (arguments.length == 1) {
            o = arguments[0];
            key = me.getKey(o);
        }
        old = me.map[key];
        if (typeof key == 'undefined' || key === null || typeof old == 'undefined') {
             return me.add(key, o);
        }
        index = me.indexOfKey(key);
        me.items[index] = o;
        me.map[key] = o;
        me.fireEvent('replace', key, old, o);
        return o;
    },

    
    addAll: function(objs){
        var me = this,
            i = 0,
            args,
            len,
            key;

        if (arguments.length > 1 || Ext.isArray(objs)) {
            args = arguments.length > 1 ? arguments : objs;
            for (len = args.length; i < len; i++) {
                me.add(args[i]);
            }
        } else {
            for (key in objs) {
                if (objs.hasOwnProperty(key)) {
                    if (me.allowFunctions || typeof objs[key] != 'function') {
                        me.add(key, objs[key]);
                    }
                }
            }
        }
    },

    
    each: function(fn, scope){
        var items = [].concat(this.items), 
            i = 0,
            len = items.length,
            item;

        for (; i < len; i++) {
            item = items[i];
            if (fn.call(scope || item, item, i, len) === false) {
                break;
            }
        }
    },

    
    eachKey: function(fn, scope){
        var keys = this.keys,
            items = this.items,
            i = 0,
            len = keys.length;

        for (; i < len; i++) {
            fn.call(scope || window, keys[i], items[i], i, len);
        }
    },

    
    findBy: function(fn, scope) {
        var keys = this.keys,
            items = this.items,
            i = 0,
            len = items.length;

        for (; i < len; i++) {
            if (fn.call(scope || window, items[i], keys[i])) {
                return items[i];
            }
        }
        return null;
    },

    
    insert: function(index, key, obj){
        var me = this,
            myKey = key,
            myObj = obj;

        if (arguments.length == 2) {
            myObj = myKey;
            myKey = me.getKey(myObj);
        }
        if (me.containsKey(myKey)) {
            me.suspendEvents();
            me.removeAtKey(myKey);
            me.resumeEvents();
        }
        if (index >= me.length) {
            return me.add(myKey, myObj);
        }
        me.length++;
        Ext.Array.splice(me.items, index, 0, myObj);
        if (typeof myKey != 'undefined' && myKey !== null) {
            me.map[myKey] = myObj;
        }
        Ext.Array.splice(me.keys, index, 0, myKey);
        me.fireEvent('add', index, myObj, myKey);
        return myObj;
    },

    
    remove: function(o){
        return this.removeAt(this.indexOf(o));
    },

    
    removeAll: function(items){
        Ext.each(items || [], function(item) {
            this.remove(item);
        }, this);

        return this;
    },

    
    removeAt: function(index){
        var me = this,
            o,
            key;

        if (index < me.length && index >= 0) {
            me.length--;
            o = me.items[index];
            Ext.Array.erase(me.items, index, 1);
            key = me.keys[index];
            if (typeof key != 'undefined') {
                delete me.map[key];
            }
            Ext.Array.erase(me.keys, index, 1);
            me.fireEvent('remove', o, key);
            return o;
        }
        return false;
    },

    
    removeAtKey: function(key){
        return this.removeAt(this.indexOfKey(key));
    },

    
    getCount: function(){
        return this.length;
    },

    
    indexOf: function(o){
        return Ext.Array.indexOf(this.items, o);
    },

    
    indexOfKey: function(key){
        return Ext.Array.indexOf(this.keys, key);
    },

    
    get: function(key) {
        var me = this,
            mk = me.map[key],
            item = mk !== undefined ? mk : (typeof key == 'number') ? me.items[key] : undefined;
        return typeof item != 'function' || me.allowFunctions ? item : null; 
    },

    
    getAt: function(index) {
        return this.items[index];
    },

    
    getByKey: function(key) {
        return this.map[key];
    },

    
    contains: function(o){
        return Ext.Array.contains(this.items, o);
    },

    
    containsKey: function(key){
        return typeof this.map[key] != 'undefined';
    },

    
    clear: function(){
        var me = this;

        me.length = 0;
        me.items = [];
        me.keys = [];
        me.map = {};
        me.fireEvent('clear');
    },

    
    first: function() {
        return this.items[0];
    },

    
    last: function() {
        return this.items[this.length - 1];
    },

    
    sum: function(property, root, start, end) {
        var values = this.extractValues(property, root),
            length = values.length,
            sum    = 0,
            i;

        start = start || 0;
        end   = (end || end === 0) ? end : length - 1;

        for (i = start; i <= end; i++) {
            sum += values[i];
        }

        return sum;
    },

    
    collect: function(property, root, allowNull) {
        var values = this.extractValues(property, root),
            length = values.length,
            hits   = {},
            unique = [],
            value, strValue, i;

        for (i = 0; i < length; i++) {
            value = values[i];
            strValue = String(value);

            if ((allowNull || !Ext.isEmpty(value)) && !hits[strValue]) {
                hits[strValue] = true;
                unique.push(value);
            }
        }

        return unique;
    },

    
    extractValues: function(property, root) {
        var values = this.items;

        if (root) {
            values = Ext.Array.pluck(values, root);
        }

        return Ext.Array.pluck(values, property);
    },

    
    getRange: function(start, end){
        var me = this,
            items = me.items,
            range = [],
            i;

        if (items.length < 1) {
            return range;
        }

        start = start || 0;
        end = Math.min(typeof end == 'undefined' ? me.length - 1 : end, me.length - 1);
        if (start <= end) {
            for (i = start; i <= end; i++) {
                range[range.length] = items[i];
            }
        } else {
            for (i = start; i >= end; i--) {
                range[range.length] = items[i];
            }
        }
        return range;
    },

    
    filter: function(property, value, anyMatch, caseSensitive) {
        var filters = [],
            filterFn;

        
        if (Ext.isString(property)) {
            filters.push(Ext.create('Ext.util.Filter', {
                property     : property,
                value        : value,
                anyMatch     : anyMatch,
                caseSensitive: caseSensitive
            }));
        } else if (Ext.isArray(property) || property instanceof Ext.util.Filter) {
            filters = filters.concat(property);
        }

        
        
        filterFn = function(record) {
            var isMatch = true,
                length = filters.length,
                i;

            for (i = 0; i < length; i++) {
                var filter = filters[i],
                    fn     = filter.filterFn,
                    scope  = filter.scope;

                isMatch = isMatch && fn.call(scope, record);
            }

            return isMatch;
        };

        return this.filterBy(filterFn);
    },

    
    filterBy: function(fn, scope) {
        var me = this,
            newMC  = new this.self(),
            keys   = me.keys,
            items  = me.items,
            length = items.length,
            i;

        newMC.getKey = me.getKey;

        for (i = 0; i < length; i++) {
            if (fn.call(scope || me, items[i], keys[i])) {
                newMC.add(keys[i], items[i]);
            }
        }

        return newMC;
    },

    
    findIndex: function(property, value, start, anyMatch, caseSensitive){
        if(Ext.isEmpty(value, false)){
            return -1;
        }
        value = this.createValueMatcher(value, anyMatch, caseSensitive);
        return this.findIndexBy(function(o){
            return o && value.test(o[property]);
        }, null, start);
    },

    
    findIndexBy: function(fn, scope, start){
        var me = this,
            keys = me.keys,
            items = me.items,
            i = start || 0,
            len = items.length;

        for (; i < len; i++) {
            if (fn.call(scope || me, items[i], keys[i])) {
                return i;
            }
        }
        return -1;
    },

    
    createValueMatcher: function(value, anyMatch, caseSensitive, exactMatch) {
        if (!value.exec) { 
            var er = Ext.String.escapeRegex;
            value = String(value);

            if (anyMatch === true) {
                value = er(value);
            } else {
                value = '^' + er(value);
                if (exactMatch === true) {
                    value += '$';
                }
            }
            value = new RegExp(value, caseSensitive ? '' : 'i');
        }
        return value;
    },

    
    clone: function() {
        var me = this,
            copy = new this.self(),
            keys = me.keys,
            items = me.items,
            i = 0,
            len = items.length;

        for(; i < len; i++){
            copy.add(keys[i], items[i]);
        }
        copy.getKey = me.getKey;
        return copy;
    }
});


Ext.define('Ext.util.HashMap', {
    mixins: {
        observable: 'Ext.util.Observable'
    },
    
    

    
    constructor: function(config) {
        
        
        
        

        this.callParent();

        this.mixins.observable.constructor.call(this);

        this.clear(true);
    },

    
    getCount: function() {
        return this.length;
    },

    
    getData: function(key, value) {
        
        if (value === undefined) {
            value = key;
            key = this.getKey(value);
        }

        return [key, value];
    },

    
    getKey: function(o) {
        return o.id;
    },

    
    add: function(key, value) {
        var me = this,
            data;

        if (me.containsKey(key)) {
            throw new Error('This key already exists in the HashMap');
        }

        data = this.getData(key, value);
        key = data[0];
        value = data[1];
        me.map[key] = value;
        ++me.length;
        me.fireEvent('add', me, key, value);
        return value;
    },

    
    replace: function(key, value) {
        var me = this,
            map = me.map,
            old;

        if (!me.containsKey(key)) {
            me.add(key, value);
        }
        old = map[key];
        map[key] = value;
        me.fireEvent('replace', me, key, value, old);
        return value;
    },

    
    remove: function(o) {
        var key = this.findKey(o);
        if (key !== undefined) {
            return this.removeByKey(key);
        }
        return false;
    },

    
    removeByKey: function(key) {
        var me = this,
            value;

        if (me.containsKey(key)) {
            value = me.map[key];
            delete me.map[key];
            --me.length;
            me.fireEvent('remove', me, key, value);
            return true;
        }
        return false;
    },

    
    get: function(key) {
        return this.map[key];
    },

    
    clear: function( initial) {
        var me = this;
        me.map = {};
        me.length = 0;
        if (initial !== true) {
            me.fireEvent('clear', me);
        }
        return me;
    },

    
    containsKey: function(key) {
        return this.map[key] !== undefined;
    },

    
    contains: function(value) {
        return this.containsKey(this.findKey(value));
    },

    
    getKeys: function() {
        return this.getArray(true);
    },

    
    getValues: function() {
        return this.getArray(false);
    },

    
    getArray: function(isKey) {
        var arr = [],
            key,
            map = this.map;
        for (key in map) {
            if (map.hasOwnProperty(key)) {
                arr.push(isKey ? key : map[key]);
            }
        }
        return arr;
    },

    
    each: function(fn, scope) {
        
        var items = Ext.apply({}, this.map),
            key,
            length = this.length;

        scope = scope || this;
        for (key in items) {
            if (items.hasOwnProperty(key)) {
                if (fn.call(scope, key, items[key], length) === false) {
                    break;
                }
            }
        }
        return this;
    },

    
    clone: function() {
        var hash = new Ext.util.HashMap(),
            map = this.map,
            key;

        hash.suspendEvents();
        for (key in map) {
            if (map.hasOwnProperty(key)) {
                hash.add(key, map[key]);
            }
        }
        hash.resumeEvents();
        return hash;
    },

    
    findKey: function(value) {
        var key,
            map = this.map;

        for (key in map) {
            if (map.hasOwnProperty(key) && map[key] === value) {
                return key;
            }
        }
        return undefined;
    }
});

Ext.define('Ext.AbstractManager', {

    

    requires: ['Ext.util.HashMap'],

    

    typeName: 'type',

    constructor: function(config) {
        Ext.apply(this, config || {});

        
        this.all = Ext.create('Ext.util.HashMap');

        this.types = {};
    },

    
    get : function(id) {
        return this.all.get(id);
    },

    
    register: function(item) {
        this.all.add(item);
    },

    
    unregister: function(item) {
        this.all.remove(item);
    },

    
    registerType : function(type, cls) {
        this.types[type] = cls;
        cls[this.typeName] = type;
    },

    
    isRegistered : function(type){
        return this.types[type] !== undefined;
    },

    
    create: function(config, defaultType) {
        var type        = config[this.typeName] || config.type || defaultType,
            Constructor = this.types[type];

        if (Constructor == undefined) {
            Ext.Error.raise("The '" + type + "' type has not been registered with this manager");
        }

        return new Constructor(config);
    },

    
    onAvailable : function(id, fn, scope){
        var all = this.all,
            item;
        
        if (all.containsKey(id)) {
            item = all.get(id);
            fn.call(scope || item, item);
        } else {
            all.on('add', function(map, key, item){
                if (key == id) {
                    fn.call(scope || item, item);
                    all.un('add', fn, scope);
                }
            });
        }
    },
    
    
    each: function(fn, scope){
        this.all.each(fn, scope || this);    
    },
    
    
    getCount: function(){
        return this.all.getCount();
    }
});


Ext.define('Ext.ModelManager', {
    extend: 'Ext.AbstractManager',
    alternateClassName: 'Ext.ModelMgr',
    requires: ['Ext.data.association.Association'],
    
    singleton: true,

    typeName: 'mtype',

    
    associationStack: [],

    
    registerType: function(name, config) {
        var proto = config.prototype,
            model;
        if (proto && proto.isModel) {
            
            model = config;
        } else {
            
            if (!config.extend) {
                config.extend = 'Ext.data.Model';
            }
            model = Ext.define(name, config);
        }
        this.types[name] = model;
        return model;
    },

    
    onModelDefined: function(model) {
        var stack  = this.associationStack,
            length = stack.length,
            create = [],
            association, i, created;

        for (i = 0; i < length; i++) {
            association = stack[i];

            if (association.associatedModel == model.modelName) {
                create.push(association);
            }
        }

        for (i = 0, length = create.length; i < length; i++) {
            created = create[i];
            this.types[created.ownerModel].prototype.associations.add(Ext.data.association.Association.create(created));
            Ext.Array.remove(stack, created);
        }
    },

    
    registerDeferredAssociation: function(association){
        this.associationStack.push(association);
    },

    
    getModel: function(id) {
        var model = id;
        if (typeof model == 'string') {
            model = this.types[model];
        }
        return model;
    },

    
    create: function(config, name, id) {
        var con = typeof name == 'function' ? name : this.types[name || config.name];

        return new con(config, id);
    }
}, function() {

    
    Ext.regModel = function() {
        if (Ext.isDefined(Ext.global.console)) {
            Ext.global.console.warn('Ext.regModel has been deprecated. Models can now be created by extending Ext.data.Model: Ext.define("MyModel", {extend: "Ext.data.Model", fields: []});.');
        }
        return this.ModelManager.registerType.apply(this.ModelManager, arguments);
    };
});


Ext.define('Ext.PluginManager', {
    extend: 'Ext.AbstractManager',
    alternateClassName: 'Ext.PluginMgr',
    singleton: true,
    typeName: 'ptype',

    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    

    create : function(config, defaultType){
        if (config.init) {
            return config;
        } else {
            return Ext.createByAlias('plugin.' + (config.ptype || defaultType), config);
        }

        
        
        
        
        
        
        
    },

    
    findByType: function(type, defaultsOnly) {
        var matches = [],
            types   = this.types;

        for (var name in types) {
            if (!types.hasOwnProperty(name)) {
                continue;
            }
            var item = types[name];

            if (item.type == type && (!defaultsOnly || (defaultsOnly === true && item.isDefault))) {
                matches.push(item);
            }
        }

        return matches;
    }
}, function() {
    
    Ext.preg = function() {
        return Ext.PluginManager.registerType.apply(Ext.PluginManager, arguments);
    };
});


Ext.define('Ext.util.MixedCollection', {
    extend: 'Ext.util.AbstractMixedCollection',
    mixins: {
        sortable: 'Ext.util.Sortable'
    },

    
    constructor: function() {
        var me = this;
        me.callParent(arguments);
        me.mixins.sortable.initSortable.call(me);
    },

    doSort: function(sorterFn) {
        this.sortBy(sorterFn);
    },

    
    _sort: function(property, dir, fn){
        var me = this,
            i, len,
            dsc   = String(dir).toUpperCase() == 'DESC' ? -1 : 1,

            
            c     = [],
            keys  = me.keys,
            items = me.items;

        
        fn = fn || function(a, b) {
            return a - b;
        };

        
        for(i = 0, len = items.length; i < len; i++){
            c[c.length] = {
                key  : keys[i],
                value: items[i],
                index: i
            };
        }

        
        Ext.Array.sort(c, function(a, b){
            var v = fn(a[property], b[property]) * dsc;
            if(v === 0){
                v = (a.index < b.index ? -1 : 1);
            }
            return v;
        });

        
        for(i = 0, len = c.length; i < len; i++){
            items[i] = c[i].value;
            keys[i]  = c[i].key;
        }

        me.fireEvent('sort', me);
    },

    
    sortBy: function(sorterFn) {
        var me     = this,
            items  = me.items,
            keys   = me.keys,
            length = items.length,
            temp   = [],
            i;

        
        for (i = 0; i < length; i++) {
            temp[i] = {
                key  : keys[i],
                value: items[i],
                index: i
            };
        }

        Ext.Array.sort(temp, function(a, b) {
            var v = sorterFn(a.value, b.value);
            if (v === 0) {
                v = (a.index < b.index ? -1 : 1);
            }

            return v;
        });

        
        for (i = 0; i < length; i++) {
            items[i] = temp[i].value;
            keys[i]  = temp[i].key;
        }

        me.fireEvent('sort', me, items, keys);
    },

    
    reorder: function(mapping) {
        var me = this,
            items = me.items,
            index = 0,
            length = items.length,
            order = [],
            remaining = [],
            oldIndex;

        me.suspendEvents();

        
        for (oldIndex in mapping) {
            order[mapping[oldIndex]] = items[oldIndex];
        }

        for (index = 0; index < length; index++) {
            if (mapping[index] == undefined) {
                remaining.push(items[index]);
            }
        }

        for (index = 0; index < length; index++) {
            if (order[index] == undefined) {
                order[index] = remaining.shift();
            }
        }

        me.clear();
        me.addAll(order);

        me.resumeEvents();
        me.fireEvent('sort', me);
    },

    
    sortByKey: function(dir, fn){
        this._sort('key', dir, fn || function(a, b){
            var v1 = String(a).toUpperCase(), v2 = String(b).toUpperCase();
            return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
        });
    }
});


Ext.define('Ext.data.AbstractStore', {
    requires: ['Ext.util.MixedCollection', 'Ext.data.Operation', 'Ext.util.Filter'],

    mixins: {
        observable: 'Ext.util.Observable',
        sortable: 'Ext.util.Sortable'
    },

    statics: {
        create: function(store) {
            if (!store.isStore) {
                if (!store.type) {
                    store.type = 'store';
                }
                store = Ext.createByAlias('store.' + store.type, store);
            }
            return store;
        }
    },

    remoteSort  : false,
    remoteFilter: false,

    

    
    autoLoad: false,

    
    autoSync: false,

    
    batchUpdateMode: 'operation',

    
    filterOnLoad: true,

    
    sortOnLoad: true,

    
    implicitModel: false,

    
    defaultProxyType: 'memory',

    
    isDestroyed: false,

    isStore: true,

    

    

    

    sortRoot: 'data',

    
    constructor: function(config) {
        var me = this,
            filters;

        

        

        

        

        

        

        

        
        
        

        Ext.apply(me, config);
        

        
        me.removed = [];

        me.mixins.observable.constructor.apply(me, arguments);
        me.model = Ext.ModelManager.getModel(me.model);

        
        Ext.applyIf(me, {
            modelDefaults: {}
        });

        
        if (!me.model && me.fields) {
            me.model = Ext.define('Ext.data.Store.ImplicitModel-' + (me.storeId || Ext.id()), {
                extend: 'Ext.data.Model',
                fields: me.fields,
                proxy: me.proxy || me.defaultProxyType
            });

            delete me.fields;

            me.implicitModel = true;
        }

        if (!me.model) {
            if (Ext.isDefined(Ext.global.console)) {
                Ext.global.console.warn('Store defined with no model. You may have mistyped the model name.');
            }
        }

        
        me.setProxy(me.proxy || me.model.getProxy());

        me.proxy.on('metachange', me.onMetaChange, me);

        if (me.id && !me.storeId) {
            me.storeId = me.id;
            delete me.id;
        }

        if (me.storeId) {
            Ext.data.StoreManager.register(me);
        }

        me.mixins.sortable.initSortable.call(me);

        
        filters = me.decodeFilters(me.filters);
        me.filters = Ext.create('Ext.util.MixedCollection');
        me.filters.addAll(filters);
    },

    
    setProxy: function(proxy) {
        var me = this;

        if (proxy instanceof Ext.data.proxy.Proxy) {
            proxy.setModel(me.model);
        } else {
            if (Ext.isString(proxy)) {
                proxy = {
                    type: proxy
                };
            }
            Ext.applyIf(proxy, {
                model: me.model
            });

            proxy = Ext.createByAlias('proxy.' + proxy.type, proxy);
        }

        me.proxy = proxy;

        return me.proxy;
    },

    
    getProxy: function() {
        return this.proxy;
    },

    
    onMetaChange: function(proxy, meta) {
        this.fireEvent('metachange', this, meta);
    },

    
    create: function(data, options) {
        var me = this,
            instance = Ext.ModelManager.create(Ext.applyIf(data, me.modelDefaults), me.model.modelName),
            operation;

        options = options || {};

        Ext.applyIf(options, {
            action : 'create',
            records: [instance]
        });

        operation = Ext.create('Ext.data.Operation', options);

        me.proxy.create(operation, me.onProxyWrite, me);

        return instance;
    },

    read: function() {
        return this.load.apply(this, arguments);
    },

    onProxyRead: Ext.emptyFn,

    update: function(options) {
        var me = this,
            operation;
        options = options || {};

        Ext.applyIf(options, {
            action : 'update',
            records: me.getUpdatedRecords()
        });

        operation = Ext.create('Ext.data.Operation', options);

        return me.proxy.update(operation, me.onProxyWrite, me);
    },

    
    onProxyWrite: function(operation) {
        var me = this,
            success = operation.wasSuccessful(),
            records = operation.getRecords();

        switch (operation.action) {
            case 'create':
                me.onCreateRecords(records, operation, success);
                break;
            case 'update':
                me.onUpdateRecords(records, operation, success);
                break;
            case 'destroy':
                me.onDestroyRecords(records, operation, success);
                break;
        }

        if (success) {
            me.fireEvent('write', me, operation);
            me.fireEvent('datachanged', me);
        }
        
        Ext.callback(operation.callback, operation.scope || me, [records, operation, success]);
    },


    
    destroy: function(options) {
        var me = this,
            operation;

        options = options || {};

        Ext.applyIf(options, {
            action : 'destroy',
            records: me.getRemovedRecords()
        });

        operation = Ext.create('Ext.data.Operation', options);

        return me.proxy.destroy(operation, me.onProxyWrite, me);
    },

    
    onBatchOperationComplete: function(batch, operation) {
        return this.onProxyWrite(operation);
    },

    
    onBatchComplete: function(batch, operation) {
        var me = this,
            operations = batch.operations,
            length = operations.length,
            i;

        me.suspendEvents();

        for (i = 0; i < length; i++) {
            me.onProxyWrite(operations[i]);
        }

        me.resumeEvents();

        me.fireEvent('datachanged', me);
    },

    onBatchException: function(batch, operation) {
        
        
        
        
        
    },

    
    filterNew: function(item) {
        
        return item.phantom === true && item.isValid();
    },

    
    getNewRecords: function() {
        return [];
    },

    
    getUpdatedRecords: function() {
        return [];
    },

    
    filterUpdated: function(item) {
        
        return item.dirty === true && item.phantom !== true && item.isValid();
    },

    
    getRemovedRecords: function() {
        return this.removed;
    },

    filter: function(filters, value) {

    },

    
    decodeFilters: function(filters) {
        if (!Ext.isArray(filters)) {
            if (filters === undefined) {
                filters = [];
            } else {
                filters = [filters];
            }
        }

        var length = filters.length,
            Filter = Ext.util.Filter,
            config, i;

        for (i = 0; i < length; i++) {
            config = filters[i];

            if (!(config instanceof Filter)) {
                Ext.apply(config, {
                    root: 'data'
                });

                
                if (config.fn) {
                    config.filterFn = config.fn;
                }

                
                if (typeof config == 'function') {
                    config = {
                        filterFn: config
                    };
                }

                filters[i] = new Filter(config);
            }
        }

        return filters;
    },

    clearFilter: function(supressEvent) {

    },

    isFiltered: function() {

    },

    filterBy: function(fn, scope) {

    },

    
    sync: function() {
        var me = this,
            options = {},
            toCreate = me.getNewRecords(),
            toUpdate = me.getUpdatedRecords(),
            toDestroy = me.getRemovedRecords(),
            needsSync = false;

        if (toCreate.length > 0) {
            options.create = toCreate;
            needsSync = true;
        }

        if (toUpdate.length > 0) {
            options.update = toUpdate;
            needsSync = true;
        }

        if (toDestroy.length > 0) {
            options.destroy = toDestroy;
            needsSync = true;
        }

        if (needsSync && me.fireEvent('beforesync', options) !== false) {
            me.proxy.batch(options, me.getBatchListeners());
        }

        return {
            added: toCreate,
            updated: toUpdate,
            removed: toDestroy
        };
    },


    
    getBatchListeners: function() {
        var me = this,
            listeners = {
                scope: me,
                exception: me.onBatchException
            };

        if (me.batchUpdateMode == 'operation') {
            listeners.operationcomplete = me.onBatchOperationComplete;
        } else {
            listeners.complete = me.onBatchComplete;
        }

        return listeners;
    },

    
    save: function() {
        return this.sync.apply(this, arguments);
    },

    
    load: function(options) {
        var me = this,
            operation;

        options = options || {};

        Ext.applyIf(options, {
            action : 'read',
            filters: me.filters.items,
            sorters: me.getSorters()
        });

        operation = Ext.create('Ext.data.Operation', options);

        if (me.fireEvent('beforeload', me, operation) !== false) {
            me.loading = true;
            me.proxy.read(operation, me.onProxyLoad, me);
        }

        return me;
    },

    
    afterEdit : function(record, modifiedFieldNames) {
        var me = this;

        if (me.autoSync) {
            me.sync();
        }

        me.fireEvent('update', me, record, Ext.data.Model.EDIT, modifiedFieldNames);
    },

    
    afterReject : function(record) {
        
        
        
        
        
        this.fireEvent('update', this, record, Ext.data.Model.REJECT, null);
    },

    
    afterCommit : function(record) {
        
        
        
        
        
        this.fireEvent('update', this, record, Ext.data.Model.COMMIT, null);
    },

    clearData: Ext.emptyFn,

    destroyStore: function() {
        var me = this;

        if (!me.isDestroyed) {
            if (me.storeId) {
                Ext.data.StoreManager.unregister(me);
            }
            me.clearData();
            me.data = null;
            me.tree = null;
            
            me.reader = me.writer = null;
            me.clearListeners();
            me.isDestroyed = true;

            if (me.implicitModel) {
                Ext.destroy(me.model);
            }
        }
    },

    doSort: function(sorterFn) {
        var me = this;
        if (me.remoteSort) {
            
            me.load();
        } else {
            me.data.sortBy(sorterFn);
            me.fireEvent('datachanged', me);
        }
    },

    getCount: Ext.emptyFn,

    getById: Ext.emptyFn,

    
    removeAll: Ext.emptyFn,
    
    

    
    isLoading: function() {
        return this.loading;
    }
});


Ext.define('Ext.data.Errors', {
    extend: 'Ext.util.MixedCollection',

    
    isValid: function() {
        return this.length === 0;
    },

    
    getByField: function(fieldName) {
        var errors = [],
            error, field, i;

        for (i = 0; i < this.length; i++) {
            error = this.items[i];

            if (error.field == fieldName) {
                errors.push(error);
            }
        }

        return errors;
    }
});


Ext.define('Ext.data.StoreManager', {
    extend: 'Ext.util.MixedCollection',
    alternateClassName: ['Ext.StoreMgr', 'Ext.data.StoreMgr', 'Ext.StoreManager'],
    singleton: true,
    uses: ['Ext.data.ArrayStore'],
    
    

    
    register : function() {
        for (var i = 0, s; (s = arguments[i]); i++) {
            this.add(s);
        }
    },

    
    unregister : function() {
        for (var i = 0, s; (s = arguments[i]); i++) {
            this.remove(this.lookup(s));
        }
    },

    
    lookup : function(store) {
        
        if (Ext.isArray(store)) {
            var fields = ['field1'], 
                expand = !Ext.isArray(store[0]),
                data = store,
                i,
                len;
                
            if(expand){
                data = [];
                for (i = 0, len = store.length; i < len; ++i) {
                    data.push([store[i]]);
                }
            } else {
                for(i = 2, len = store[0].length; i <= len; ++i){
                    fields.push('field' + i);
                }
            }
            return Ext.create('Ext.data.ArrayStore', {
                data  : data,
                fields: fields,
                autoDestroy: true,
                autoCreated: true,
                expanded: expand
            });
        }
        
        if (Ext.isString(store)) {
            
            return this.get(store);
        } else {
            
            return Ext.data.AbstractStore.create(store);
        }
    },

    
    getKey : function(o) {
         return o.storeId;
    }
}, function() {    
    
    Ext.regStore = function(name, config) {
        var store;

        if (Ext.isObject(name)) {
            config = name;
        } else {
            config.storeId = name;
        }

        if (config instanceof Ext.data.Store) {
            store = config;
        } else {
            store = Ext.create('Ext.data.Store', config);
        }

        return Ext.data.StoreManager.register(store);
    };

    
    Ext.getStore = function(name) {
        return Ext.data.StoreManager.lookup(name);
    };
});


Ext.define('Ext.data.proxy.Ajax', {
    requires: ['Ext.util.MixedCollection', 'Ext.Ajax'],
    extend: 'Ext.data.proxy.Server',
    alias: 'proxy.ajax',
    alternateClassName: ['Ext.data.HttpProxy', 'Ext.data.AjaxProxy'],
    
    
    actionMethods: {
        create : 'POST',
        read   : 'GET',
        update : 'POST',
        destroy: 'POST'
    },
    
    
    
    
    doRequest: function(operation, callback, scope) {
        var writer  = this.getWriter(),
            request = this.buildRequest(operation, callback, scope);
            
        if (operation.allowWrite()) {
            request = writer.write(request);
        }
        
        Ext.apply(request, {
            headers       : this.headers,
            timeout       : this.timeout,
            scope         : this,
            callback      : this.createRequestCallback(request, operation, callback, scope),
            method        : this.getMethod(request),
            disableCaching: false 
        });
        
        Ext.Ajax.request(request);
        
        return request;
    },
    
    
    getMethod: function(request) {
        return this.actionMethods[request.action];
    },
    
    
    createRequestCallback: function(request, operation, callback, scope) {
        var me = this;
        
        return function(options, success, response) {
            me.processResponse(success, operation, request, response, callback, scope);
        };
    }
}, function() {
    
    Ext.data.HttpProxy = this;
});


Ext.define('Ext.data.Model', {
    alternateClassName: 'Ext.data.Record',

    mixins: {
        observable: 'Ext.util.Observable'
    },

    requires: [
        'Ext.ModelManager',
        'Ext.data.IdGenerator',
        'Ext.data.Field',
        'Ext.data.Errors',
        'Ext.data.Operation',
        'Ext.data.validations',
        'Ext.data.proxy.Ajax',
        'Ext.util.MixedCollection'
    ],

    onClassExtended: function(cls, data, hooks) {
        var onBeforeClassCreated = hooks.onBeforeCreated;

        hooks.onBeforeCreated = function(cls, data) {
            var me = this,
                name = Ext.getClassName(cls),
                prototype = cls.prototype,
                superCls = cls.prototype.superclass,

                validations = data.validations || [],
                fields = data.fields || [],
                associations = data.associations || [],
                belongsTo = data.belongsTo,
                hasMany = data.hasMany,
                hasOne = data.hasOne,
                addAssociations = function(items, type) {
                    var i = 0,
                        len,
                        item;

                    if (items) {
                        items = Ext.Array.from(items);

                        for (len = items.length; i < len; ++i) {
                            item = items[i];

                            if (!Ext.isObject(item)) {
                                item = {model: item};
                            }

                            item.type = type;
                            associations.push(item);
                        }
                    }
                },
                idgen = data.idgen,

                fieldsMixedCollection = new Ext.util.MixedCollection(false, function(field) {
                    return field.name;
                }),

                associationsMixedCollection = new Ext.util.MixedCollection(false, function(association) {
                    return association.name;
                }),

                superValidations = superCls.validations,
                superFields = superCls.fields,
                superAssociations = superCls.associations,

                association, i, ln,
                dependencies = [];

            
            cls.modelName = name;
            prototype.modelName = name;

            
            if (superValidations) {
                validations = superValidations.concat(validations);
            }

            data.validations = validations;

            
            if (superFields) {
                fields = superFields.items.concat(fields);
            }

            for (i = 0, ln = fields.length; i < ln; ++i) {
                fieldsMixedCollection.add(new Ext.data.Field(fields[i]));
            }

            data.fields = fieldsMixedCollection;

            if (idgen) {
                data.idgen = Ext.data.IdGenerator.get(idgen);
            }

            
            
            addAssociations(data.belongsTo, 'belongsTo');
            delete data.belongsTo;
            addAssociations(data.hasMany, 'hasMany');
            delete data.hasMany;
            addAssociations(data.hasOne, 'hasOne');
            delete data.hasOne;

            if (superAssociations) {
                associations = superAssociations.items.concat(associations);
            }

            for (i = 0, ln = associations.length; i < ln; ++i) {
                dependencies.push('association.' + associations[i].type.toLowerCase());
            }

            if (data.proxy) {
                if (typeof data.proxy === 'string') {
                    dependencies.push('proxy.' + data.proxy);
                }
                else if (typeof data.proxy.type === 'string') {
                    dependencies.push('proxy.' + data.proxy.type);
                }
            }

            Ext.require(dependencies, function() {
                Ext.ModelManager.registerType(name, cls);

                for (i = 0, ln = associations.length; i < ln; ++i) {
                    association = associations[i];

                    Ext.apply(association, {
                        ownerModel: name,
                        associatedModel: association.model
                    });

                    if (Ext.ModelManager.getModel(association.model) === undefined) {
                        Ext.ModelManager.registerDeferredAssociation(association);
                    } else {
                        associationsMixedCollection.add(Ext.data.association.Association.create(association));
                    }
                }

                data.associations = associationsMixedCollection;

                onBeforeClassCreated.call(me, cls, data, hooks);

                cls.setProxy(cls.prototype.proxy || cls.prototype.defaultProxyType);

                
                Ext.ModelManager.onModelDefined(cls);
            });
        };
    },

    inheritableStatics: {
        
        setProxy: function(proxy) {
            
            if (!proxy.isProxy) {
                if (typeof proxy == "string") {
                    proxy = {
                        type: proxy
                    };
                }
                proxy = Ext.createByAlias("proxy." + proxy.type, proxy);
            }
            proxy.setModel(this);
            this.proxy = this.prototype.proxy = proxy;

            return proxy;
        },

        
        getProxy: function() {
            return this.proxy;
        },

        
        setFields: function(fields) {
            var me = this,
                prototypeFields = me.prototype.fields,
                len = fields.length,
                i = 0;

            if (prototypeFields) {
                prototypeFields.clear();
            }
            else {
                prototypeFields = me.prototype.fields = new Ext.util.MixedCollection(false, function(field) {
                    return field.name;
                });
            }

            for (; i < len; i++) {
                prototypeFields.add(new Ext.data.Field(fields[i]));
            }

            me.fields = prototypeFields;

            return prototypeFields;
        },

        getFields: function() {
            return this.fields;
        },

        
        load: function(id, config) {
            config = Ext.apply({}, config);
            config = Ext.applyIf(config, {
                action: 'read',
                id    : id
            });

            var operation  = Ext.create('Ext.data.Operation', config),
                scope      = config.scope || this,
                record     = null,
                callback;

            callback = function(operation) {
                if (operation.wasSuccessful()) {
                    record = operation.getRecords()[0];
                    Ext.callback(config.success, scope, [record, operation]);
                } else {
                    Ext.callback(config.failure, scope, [record, operation]);
                }
                Ext.callback(config.callback, scope, [record, operation]);
            };

            this.proxy.read(operation, callback, this);
        }
    },

    statics: {
        PREFIX : 'ext-record',
        AUTO_ID: 1,
        EDIT   : 'edit',
        REJECT : 'reject',
        COMMIT : 'commit',

        
        id: function(rec) {
            var id = [this.PREFIX, '-', this.AUTO_ID++].join('');
            rec.phantom = true;
            rec.internalId = id;
            return id;
        }
    },

    
    idgen: {
        isGenerator: true,
        type: 'default',

        generate: function () {
            return null;
        },
        getRecId: function (rec) {
            return rec.modelName + '-' + rec.internalId;
        }
    },

    
    editing : false,

    
    dirty : false,

    
    persistenceProperty: 'data',

    evented: false,
    isModel: true,

    
    phantom : false,

    
    idProperty: 'id',

    
    defaultProxyType: 'ajax',

    
    
    

    

    
    
    
    
    

    
    constructor: function(data, id, raw) {
        data = data || {};

        var me = this,
            fields,
            length,
            field,
            name,
            i,
            newId,
            isArray = Ext.isArray(data),
            newData = isArray ? {} : null; 

        
        me.internalId = (id || id === 0) ? id : Ext.data.Model.id(me);

        
        me.raw = raw;

        Ext.applyIf(me, {
            data: {}
        });

        
        me.modified = {};

        
        if (me.persistanceProperty) {
            if (Ext.isDefined(Ext.global.console)) {
                Ext.global.console.warn('Ext.data.Model: persistanceProperty has been deprecated. Use persistenceProperty instead.');
            }
            me.persistenceProperty = me.persistanceProperty;
        }
        me[me.persistenceProperty] = {};

        me.mixins.observable.constructor.call(me);

        
        fields = me.fields.items;
        length = fields.length;

        for (i = 0; i < length; i++) {
            field = fields[i];
            name  = field.name;

            if (isArray){
                
                
                newData[name] = data[i];
            }
            else if (data[name] === undefined) {
                data[name] = field.defaultValue;
            }
        }

        me.set(newData || data);

        if (me.getId()) {
            me.phantom = false;
        } else if (me.phantom) {
            newId = me.idgen.generate();
            if (newId !== null) {
                me.setId(newId);
            }
        }

        
        me.dirty = false;
        me.modified = {};

        if (typeof me.init == 'function') {
            me.init();
        }

        me.id = me.idgen.getRecId(me);
    },

    
    get: function(field) {
        return this[this.persistenceProperty][field];
    },

    
    set: function(fieldName, value) {
        var me = this,
            fields = me.fields,
            modified = me.modified,
            modifiedFieldNames = [],
            field, key, i, currentValue, notEditing, count, length;

        
        if (arguments.length == 1 && Ext.isObject(fieldName)) {
            notEditing = !me.editing;
            count = 0;
            for (key in fieldName) {
                if (fieldName.hasOwnProperty(key)) {

                    
                    
                    field = fields.get(key);
                    if (field && field.convert !== field.type.convert) {
                        modifiedFieldNames.push(key);
                        continue;
                    }

                    if (!count && notEditing) {
                        me.beginEdit();
                    }
                    ++count;
                    me.set(key, fieldName[key]);
                }
            }

            length = modifiedFieldNames.length;
            if (length) {
                if (!count && notEditing) {
                    me.beginEdit();
                }
                count += length;
                for (i = 0; i < length; i++) {
                    field = modifiedFieldNames[i];
                    me.set(field, fieldName[field]);
                }
            }

            if (notEditing && count) {
                me.endEdit(false, modifiedFieldNames);
            }
        } else {
            if (fields) {
                field = fields.get(fieldName);

                if (field && field.convert) {
                    value = field.convert(value, me);
                }
            }
            currentValue = me.get(fieldName);
            me[me.persistenceProperty][fieldName] = value;

            if (field && field.persist && !me.isEqual(currentValue, value)) {
                if (me.isModified(fieldName)) {
                    if (me.isEqual(modified[fieldName], value)) {
                        
                        
                        delete modified[fieldName];
                        
                        
                        me.dirty = false;
                        for (key in modified) {
                            if (modified.hasOwnProperty(key)){
                                me.dirty = true;
                                break;
                            }
                        }
                    }
                } else {
                    me.dirty = true;
                    modified[fieldName] = currentValue;
                }
            }

            if (!me.editing) {
                me.afterEdit([fieldName]);
            }
        }
    },

    
    isEqual: function(a, b){
        if (Ext.isDate(a) && Ext.isDate(b)) {
            return a.getTime() === b.getTime();
        }
        return a === b;
    },

    
    beginEdit : function(){
        var me = this;
        if (!me.editing) {
            me.editing = true;
            me.dirtySave = me.dirty;
            me.dataSave = Ext.apply({}, me[me.persistenceProperty]);
            me.modifiedSave = Ext.apply({}, me.modified);
        }
    },

    
    cancelEdit : function(){
        var me = this;
        if (me.editing) {
            me.editing = false;
            
            me.modified = me.modifiedSave;
            me[me.persistenceProperty] = me.dataSave;
            me.dirty = me.dirtySave;
            delete me.modifiedSave;
            delete me.dataSave;
            delete me.dirtySave;
        }
    },

    
    endEdit : function(silent, modifiedFieldNames){
        var me = this;
        if (me.editing) {
            me.editing = false;
            delete me.modifiedSave;
            delete me.dataSave;
            delete me.dirtySave;
            if (silent !== true && me.dirty) {
                me.afterEdit(modifiedFieldNames);
            }
        }
    },

    
    getChanges : function(){
        var modified = this.modified,
            changes  = {},
            field;

        for (field in modified) {
            if (modified.hasOwnProperty(field)){
                changes[field] = this.get(field);
            }
        }

        return changes;
    },

    
    isModified : function(fieldName) {
        return this.modified.hasOwnProperty(fieldName);
    },

    
    setDirty : function() {
        var me = this,
            name;

        me.dirty = true;

        me.fields.each(function(field) {
            if (field.persist) {
                name = field.name;
                me.modified[name] = me.get(name);
            }
        }, me);
    },

    markDirty : function() {
        if (Ext.isDefined(Ext.global.console)) {
            Ext.global.console.warn('Ext.data.Model: markDirty has been deprecated. Use setDirty instead.');
        }
        return this.setDirty.apply(this, arguments);
    },

    
    reject : function(silent) {
        var me = this,
            modified = me.modified,
            field;

        for (field in modified) {
            if (modified.hasOwnProperty(field)) {
                if (typeof modified[field] != "function") {
                    me[me.persistenceProperty][field] = modified[field];
                }
            }
        }

        me.dirty = false;
        me.editing = false;
        me.modified = {};

        if (silent !== true) {
            me.afterReject();
        }
    },

    
    commit : function(silent) {
        var me = this;

        me.phantom = me.dirty = me.editing = false;
        me.modified = {};

        if (silent !== true) {
            me.afterCommit();
        }
    },

    
    copy : function(newId) {
        var me = this;

        return new me.self(Ext.apply({}, me[me.persistenceProperty]), newId || me.internalId);
    },

    
    setProxy: function(proxy) {
        
        if (!proxy.isProxy) {
            if (typeof proxy === "string") {
                proxy = {
                    type: proxy
                };
            }
            proxy = Ext.createByAlias("proxy." + proxy.type, proxy);
        }
        proxy.setModel(this.self);
        this.proxy = proxy;

        return proxy;
    },

    
    getProxy: function() {
        return this.proxy;
    },

    
    validate: function() {
        var errors      = Ext.create('Ext.data.Errors'),
            validations = this.validations,
            validators  = Ext.data.validations,
            length, validation, field, valid, type, i;

        if (validations) {
            length = validations.length;

            for (i = 0; i < length; i++) {
                validation = validations[i];
                field = validation.field || validation.name;
                type  = validation.type;
                valid = validators[type](validation, this.get(field));

                if (!valid) {
                    errors.add({
                        field  : field,
                        message: validation.message || validators[type + 'Message']
                    });
                }
            }
        }

        return errors;
    },

    
    isValid: function(){
        return this.validate().isValid();
    },

    
    save: function(options) {
        options = Ext.apply({}, options);

        var me     = this,
            action = me.phantom ? 'create' : 'update',
            record = null,
            scope  = options.scope || me,
            operation,
            callback;

        Ext.apply(options, {
            records: [me],
            action : action
        });

        operation = Ext.create('Ext.data.Operation', options);

        callback = function(operation) {
            if (operation.wasSuccessful()) {
                record = operation.getRecords()[0];
                
                
                me.set(record.data);
                record.dirty = false;

                Ext.callback(options.success, scope, [record, operation]);
            } else {
                Ext.callback(options.failure, scope, [record, operation]);
            }

            Ext.callback(options.callback, scope, [record, operation]);
        };

        me.getProxy()[action](operation, callback, me);

        return me;
    },

    
    destroy: function(options){
        options = Ext.apply({}, options);

        var me     = this,
            record = null,
            scope  = options.scope || me,
            operation,
            callback;

        Ext.apply(options, {
            records: [me],
            action : 'destroy'
        });

        operation = Ext.create('Ext.data.Operation', options);
        callback = function(operation) {
            if (operation.wasSuccessful()) {
                Ext.callback(options.success, scope, [record, operation]);
            } else {
                Ext.callback(options.failure, scope, [record, operation]);
            }
            Ext.callback(options.callback, scope, [record, operation]);
        };

        me.getProxy().destroy(operation, callback, me);
        return me;
    },

    
    getId: function() {
        return this.get(this.idProperty);
    },

    getUniqueId: function() {
        var id = this.uniqueId,
            currentId;

        if (!id) {
            currentId = this.id;
            this.id = null;
            id = this.uniqueId = this.mixins.identifiable.getUniqueId.call(this);
            this.id = currentId;
        }

        return id;
    },

    
    setId: function(id) {
        this.set(this.idProperty, id);
    },

    
    join : function(store) {
        
        this.store = store;
    },

    
    unjoin: function(store) {
        delete this.store;
    },

    
    afterEdit : function(modifiedFieldNames) {
        this.callStore('afterEdit', modifiedFieldNames);
    },

    
    afterReject : function() {
        this.callStore("afterReject");
    },

    
    afterCommit: function() {
        this.callStore('afterCommit');
    },

    
    callStore: function(fn) {
        var store = this.store,
            args = Ext.Array.clone(arguments);

        args[0] = this;
        if (store !== undefined && typeof store[fn] == "function") {
            store[fn].apply(store, args);
        }
    },

    
    getData: function(includeAssociated){
        var me = this,
            data = {},
            name;

        me.fields.each(function(field) {
            name = field.name;
            data[name] = me.get(name);
        }, me);

        if (includeAssociated === true) {
            Ext.apply(data, me.getAssociatedData());
        }
        return data;
    },

    
    getAssociatedData: function(){
        return this.prepareAssociatedData(this, [], null);
    },

    
    prepareAssociatedData: function(record, ids, associationType) {
        
        var associations     = record.associations.items,
            associationCount = associations.length,
            associationData  = {},
            associatedStore, associatedName, associatedRecords, associatedRecord,
            associatedRecordCount, association, id, i, j, type, allow;

        for (i = 0; i < associationCount; i++) {
            association = associations[i];
            type = association.type;
            allow = true;
            if (associationType) {
                allow = type == associationType;
            }
            if (allow && type == 'hasMany') {

                
                associatedStore = record[association.storeName];

                
                associationData[association.name] = [];

                
                if (associatedStore && associatedStore.getCount() > 0) {
                    associatedRecords = associatedStore.data.items;
                    associatedRecordCount = associatedRecords.length;

                    
                    for (j = 0; j < associatedRecordCount; j++) {
                        associatedRecord = associatedRecords[j];
                        
                        id = associatedRecord.id;

                        
                        
                        if (Ext.Array.indexOf(ids, id) == -1) {
                            ids.push(id);

                            associationData[association.name][j] = associatedRecord.getData();
                            Ext.apply(associationData[association.name][j], this.prepareAssociatedData(associatedRecord, ids, type));
                        }
                    }
                }
            } else if (allow && (type == 'belongsTo' || type == 'hasOne')) {
                associatedRecord = record[association.instanceName];
                if (associatedRecord !== undefined) {
                    id = associatedRecord.id;
                    if (Ext.Array.indexOf(ids, id) === -1) {
                        ids.push(id);
                        associationData[association.name] = associatedRecord.getData();
                        Ext.apply(associationData[association.name], this.prepareAssociatedData(associatedRecord, ids, type));
                    }
                }
            }
        }

        return associationData;
    }
});


Ext.define('Ext.data.Store', {
    extend: 'Ext.data.AbstractStore',

    alias: 'store.store',

    requires: ['Ext.data.StoreManager', 'Ext.ModelManager', 'Ext.data.Model', 'Ext.util.Grouper'],
    uses: ['Ext.data.proxy.Memory'],

    
    remoteSort: false,

    
    remoteFilter: false,

    
    remoteGroup : false,

    

    

    
    groupField: undefined,

    
    groupDir: "ASC",

    
    pageSize: 25,

    
    currentPage: 1,

    
    clearOnPageLoad: true,

    
    loading: false,

    
    sortOnFilter: true,

    
    buffered: false,

    
    purgePageCount: 5,

    isStore: true,

    onClassExtended: function(cls, data, hooks) {
        var model = data.model;

        if (typeof model == 'string') {
            var onBeforeClassCreated = hooks.onBeforeCreated;

            hooks.onBeforeCreated = function() {
                var me = this,
                    args = arguments;

                Ext.require(model, function() {
                    onBeforeClassCreated.apply(me, args);
                });
            };
        }
    },

    
    constructor: function(config) {
        config = config || {};

        var me = this,
            groupers = config.groupers || me.groupers,
            groupField = config.groupField || me.groupField,
            proxy,
            data;

        if (config.buffered || me.buffered) {
            me.prefetchData = Ext.create('Ext.util.MixedCollection', false, function(record) {
                return record.index;
            });
            me.pendingRequests = [];
            me.pagesRequested = [];

            me.sortOnLoad = false;
            me.filterOnLoad = false;
        }

        
        
        
        data = config.data || me.data;

        
        me.data = Ext.create('Ext.util.MixedCollection', false, function(record) {
            return record.internalId;
        });

        if (data) {
            me.inlineData = data;
            delete config.data;
        }

        if (!groupers && groupField) {
            groupers = [
                {
                    property : groupField,
                    direction: config.groupDir || me.groupDir
                }
            ];
        }
        delete config.groupers;

        
        me.groupers = Ext.create('Ext.util.MixedCollection');
        me.groupers.addAll(me.decodeGroupers(groupers));

        this.callParent([config]);
        

        if (me.groupers.items.length) {
            me.sort(me.groupers.items, 'prepend', false);
        }

        proxy = me.proxy;
        data = me.inlineData;

        if (data) {
            if (proxy instanceof Ext.data.proxy.Memory) {
                proxy.data = data;
                me.read();
            } else {
                me.add.apply(me, data);
            }

            me.sort();
            delete me.inlineData;
        } else if (me.autoLoad) {
            Ext.defer(me.load, 10, me, [typeof me.autoLoad === 'object' ? me.autoLoad : undefined]);
            
            
        }
    },

    onBeforeSort: function() {
        var groupers = this.groupers;
        if (groupers.getCount() > 0) {
            this.sort(groupers.items, 'prepend', false);
        }
    },

    
    decodeGroupers: function(groupers) {
        if (!Ext.isArray(groupers)) {
            if (groupers === undefined) {
                groupers = [];
            } else {
                groupers = [groupers];
            }
        }

        var length = groupers.length,
            Grouper = Ext.util.Grouper,
            config, i;

        for (i = 0; i < length; i++) {
            config = groupers[i];

            if (!(config instanceof Grouper)) {
                if (Ext.isString(config)) {
                    config = {
                        property: config
                    };
                }

                Ext.applyIf(config, {
                    root     : 'data',
                    direction: "ASC"
                });

                
                if (config.fn) {
                    config.sorterFn = config.fn;
                }

                
                if (typeof config == 'function') {
                    config = {
                        sorterFn: config
                    };
                }

                groupers[i] = new Grouper(config);
            }
        }

        return groupers;
    },

    
    group: function(groupers, direction) {
        var me = this,
            hasNew = false,
            grouper,
            newGroupers;

        if (Ext.isArray(groupers)) {
            newGroupers = groupers;
        } else if (Ext.isObject(groupers)) {
            newGroupers = [groupers];
        } else if (Ext.isString(groupers)) {
            grouper = me.groupers.get(groupers);

            if (!grouper) {
                grouper = {
                    property : groupers,
                    direction: direction
                };
                newGroupers = [grouper];
            } else if (direction === undefined) {
                grouper.toggle();
            } else {
                grouper.setDirection(direction);
            }
        }

        if (newGroupers && newGroupers.length) {
            hasNew = true;
            newGroupers = me.decodeGroupers(newGroupers);
            me.groupers.clear();
            me.groupers.addAll(newGroupers);
        }

        if (me.remoteGroup) {
            me.load({
                scope: me,
                callback: me.fireGroupChange
            });
        } else {
            
            me.sort(null, null, null, hasNew);
            me.fireGroupChange();
        }
    },

    
    clearGrouping: function() {
        var me = this;
        
        me.groupers.each(function(grouper) {
            me.sorters.remove(grouper);
        });
        me.groupers.clear();
        if (me.remoteGroup) {
            me.load({
                scope: me,
                callback: me.fireGroupChange
            });
        } else {
            me.sort();
            me.fireEvent('groupchange', me, me.groupers);
        }
    },

    
    isGrouped: function() {
        return this.groupers.getCount() > 0;
    },

    
    fireGroupChange: function() {
        this.fireEvent('groupchange', this, this.groupers);
    },

    
    getGroups: function(requestGroupString) {
        var records = this.data.items,
            length = records.length,
            groups = [],
            pointers = {},
            record,
            groupStr,
            group,
            i;

        for (i = 0; i < length; i++) {
            record = records[i];
            groupStr = this.getGroupString(record);
            group = pointers[groupStr];

            if (group === undefined) {
                group = {
                    name: groupStr,
                    children: []
                };

                groups.push(group);
                pointers[groupStr] = group;
            }

            group.children.push(record);
        }

        return requestGroupString ? pointers[requestGroupString] : groups;
    },

    
    getGroupsForGrouper: function(records, grouper) {
        var length = records.length,
            groups = [],
            oldValue,
            newValue,
            record,
            group,
            i;

        for (i = 0; i < length; i++) {
            record = records[i];
            newValue = grouper.getGroupString(record);

            if (newValue !== oldValue) {
                group = {
                    name: newValue,
                    grouper: grouper,
                    records: []
                };
                groups.push(group);
            }

            group.records.push(record);

            oldValue = newValue;
        }

        return groups;
    },

    
    getGroupsForGrouperIndex: function(records, grouperIndex) {
        var me = this,
            groupers = me.groupers,
            grouper = groupers.getAt(grouperIndex),
            groups = me.getGroupsForGrouper(records, grouper),
            length = groups.length,
            i;

        if (grouperIndex + 1 < groupers.length) {
            for (i = 0; i < length; i++) {
                groups[i].children = me.getGroupsForGrouperIndex(groups[i].records, grouperIndex + 1);
            }
        }

        for (i = 0; i < length; i++) {
            groups[i].depth = grouperIndex;
        }

        return groups;
    },

    
    getGroupData: function(sort) {
        var me = this;
        if (sort !== false) {
            me.sort();
        }

        return me.getGroupsForGrouperIndex(me.data.items, 0);
    },

    
    getGroupString: function(instance) {
        var group = this.groupers.first();
        if (group) {
            return instance.get(group.property);
        }
        return '';
    },
    
    insert: function(index, records) {
        var me = this,
            sync = false,
            i,
            record,
            len;

        records = [].concat(records);
        for (i = 0,len = records.length; i < len; i++) {
            record = me.createModel(records[i]);
            record.set(me.modelDefaults);
            
            records[i] = record;

            me.data.insert(index + i, record);
            record.join(me);

            sync = sync || record.phantom === true;
        }

        if (me.snapshot) {
            me.snapshot.addAll(records);
        }

        me.fireEvent('add', me, records, index);
        me.fireEvent('datachanged', me);
        if (me.autoSync && sync) {
            me.sync();
        }
    },

    
    add: function(records) {
        
        if (!Ext.isArray(records)) {
            records = Array.prototype.slice.apply(arguments);
        }

        var me = this,
            i = 0,
            length = records.length,
            record;

        for (; i < length; i++) {
            record = me.createModel(records[i]);
            
            records[i] = record;
        }

        me.insert(me.data.length, records);

        return records;
    },

    
    sync: function() {
        if (typeof this.proxy.sync != 'function') {
            this.callParent(arguments);
        }else{
            this.proxy.sync(this);
        }
    },

    
    createModel: function(record) {
        if (!record.isModel) {
            record = Ext.ModelManager.create(record, this.model);
        }

        return record;
    },

    
    each: function(fn, scope) {
        this.data.each(fn, scope);
    },

    
    remove: function(records,  isMove) {
        if (!Ext.isArray(records)) {
            records = [records];
        }

        
        isMove = isMove === true;
        var me = this,
            sync = false,
            i = 0,
            length = records.length,
            isPhantom,
            index,
            record;

        for (; i < length; i++) {
            record = records[i];
            index = me.data.indexOf(record);

            if (me.snapshot) {
                me.snapshot.remove(record);
            }

            if (index > -1) {
                isPhantom = record.phantom === true;
                if (!isMove && !isPhantom) {
                    
                    me.removed.push(record);
                }

                record.unjoin(me);
                me.data.remove(record);
                sync = sync || !isPhantom;

                me.fireEvent('remove', me, record, index);
            }
        }

        me.fireEvent('datachanged', me);
        if (!isMove && me.autoSync && sync) {
            me.sync();
        }
    },

    
    removeAt: function(index) {
        var record = this.getAt(index);

        if (record) {
            this.remove(record);
        }
    },

    
    load: function(options) {
        var me = this;

        options = options || {};

        if (Ext.isFunction(options)) {
            options = {
                callback: options
            };
        }

        Ext.applyIf(options, {
            groupers: me.groupers.items,
            page: me.currentPage,
            start: (me.currentPage - 1) * me.pageSize,
            limit: me.pageSize,
            addRecords: false
        });

        return me.callParent([options]);
    },

    
    onProxyLoad: function(operation) {
        var me = this,
            resultSet = operation.getResultSet(),
            records = operation.getRecords(),
            successful = operation.wasSuccessful();

        if (resultSet) {
            me.totalCount = resultSet.total;
        }

        if (successful) {
            
            me.suspendEvents();
            me.loadRecords(records, operation);
            me.resumeEvents();
        }

        me.loading = false;
        me.fireEvent('load', me, records, successful);

        
        
        me.fireEvent('read', me, records, operation.wasSuccessful());

        
        Ext.callback(operation.callback, operation.scope || me, [records, operation, successful]);
    },

    
    onCreateRecords: function(records, operation, success) {
        if (success) {
            var i = 0,
                data = this.data,
                snapshot = this.snapshot,
                length = records.length,
                originalRecords = operation.records,
                record,
                original,
                index;

            
            for (; i < length; ++i) {
                record = records[i];
                original = originalRecords[i];
                if (original) {
                    index = data.indexOf(original);
                    if (index > -1) {
                        data.removeAt(index);
                        data.insert(index, record);
                    }
                    if (snapshot) {
                        index = snapshot.indexOf(original);
                        if (index > -1) {
                            snapshot.removeAt(index);
                            snapshot.insert(index, record);
                        }
                    }
                    record.phantom = false;
                    record.join(this);
                }
            }
        }
    },

    
    onUpdateRecords: function(records, operation, success) {
        if (success) {
            var i = 0,
                length = records.length,
                data = this.data,
                snapshot = this.snapshot,
                record;

            for (; i < length; ++i) {
                record = records[i];
                data.replace(record);
                if (snapshot) {
                    snapshot.replace(record);
                }
                record.join(this);
            }
        }
    },

    
    onDestroyRecords: function(records, operation, success) {
        if (success) {
            var me = this,
                i = 0,
                length = records.length,
                data = me.data,
                snapshot = me.snapshot,
                record;

            for (; i < length; ++i) {
                record = records[i];
                record.unjoin(me);
                data.remove(record);
                if (snapshot) {
                    snapshot.remove(record);
                }
            }
            me.removed = [];
        }
    },

    
    getNewRecords: function() {
        return this.data.filterBy(this.filterNew).items;
    },

    
    getUpdatedRecords: function() {
        return this.data.filterBy(this.filterUpdated).items;
    },

    
    filter: function(filters, value) {
        if (Ext.isString(filters)) {
            filters = {
                property: filters,
                value: value
            };
        }

        var me = this,
            decoded = me.decodeFilters(filters),
            i = 0,
            doLocalSort = me.sortOnFilter && !me.remoteSort,
            length = decoded.length;

        for (; i < length; i++) {
            me.filters.replace(decoded[i]);
        }

        if (me.remoteFilter) {
            
            me.load();
        } else {
            
            if (me.filters.getCount()) {
                me.snapshot = me.snapshot || me.data.clone();
                me.data = me.data.filter(me.filters.items);

                if (doLocalSort) {
                    me.sort();
                }
                
                if (!doLocalSort || me.sorters.length < 1) {
                    me.fireEvent('filter', me);
                    me.fireEvent('datachanged', me);
                }
            }
        }
    },

    
    clearFilter: function(suppressEvent) {
        var me = this;

        me.filters.clear();

        if (me.remoteFilter) {
            me.load();
        } else if (me.isFiltered()) {
            me.data = me.snapshot.clone();
            delete me.snapshot;

            if (suppressEvent !== true) {
                me.fireEvent('filter', me);
                me.fireEvent('datachanged', me);
            }
        }
    },

    
    isFiltered: function() {
        var snapshot = this.snapshot;
        return !! snapshot && snapshot !== this.data;
    },

    
    filterBy: function(fn, scope) {
        var me = this;

        me.snapshot = me.snapshot || me.data.clone();
        me.data = me.queryBy(fn, scope || me);
        me.fireEvent('filter', me);
        me.fireEvent('datachanged', me);
    },

    
    queryBy: function(fn, scope) {
        var me = this,
            data = me.snapshot || me.data;
        return data.filterBy(fn, scope || me);
    },

    
    loadData: function(data, append) {
        var model = this.model,
            length = data.length,
            i,
            record;

        
        for (i = 0; i < length; i++) {
            record = data[i];

            if (! (record instanceof Ext.data.Model)) {
                data[i] = Ext.ModelManager.create(record, model);
            }
        }

        this.loadRecords(data, {addRecords: append});
    },

    
    loadRecords: function(records, options) {
        var me = this,
            i = 0,
            length = records.length;

        options = options || {};


        if (!options.addRecords) {
            me.removeAll();
        }

        me.add(records);

        for (; i < length; i++) {
            if (options.start !== undefined) {
                records[i].index = options.start + i;

            }
            records[i].join(me);
        }

        
        me.suspendEvents();

        if (me.filterOnLoad && !me.remoteFilter) {
            me.filter();
        }

        if (me.sortOnLoad && !me.remoteSort) {
            me.sort();
        }

        me.resumeEvents();
        me.fireEvent('datachanged', me, records);
    },

    
    
    loadPage: function(page, options) {
        var me = this;
        options = Ext.apply({}, options);

        me.currentPage = page;

        me.read(Ext.applyIf(options, {
            page: page,
            start: (page - 1) * me.pageSize,
            limit: me.pageSize,
            addRecords: !me.clearOnPageLoad
        }));
    },

    
    nextPage: function(options) {
        this.loadPage(this.currentPage + 1, options);
    },

    
    previousPage: function(options) {
        this.loadPage(this.currentPage - 1, options);
    },

    
    clearData: function() {
        var me = this;
        me.data.each(function(record) {
            record.unjoin(me);
        });

        me.data.clear();
    },

    
    
    prefetch: function(options) {
        var me = this,
            operation,
            requestId = me.getRequestId();

        options = options || {};

        Ext.applyIf(options, {
            action : 'read',
            filters: me.filters.items,
            sorters: me.sorters.items,
            requestId: requestId
        });
        me.pendingRequests.push(requestId);

        operation = Ext.create('Ext.data.Operation', options);

        
        
        
        
        if (me.fireEvent('beforeprefetch', me, operation) !== false) {
            me.loading = true;
            me.proxy.read(operation, me.onProxyPrefetch, me);
        }

        return me;
    },

    
    prefetchPage: function(page, options) {
        var me = this,
            pageSize = me.pageSize,
            start = (page - 1) * me.pageSize,
            end = start + pageSize;

        
        if (Ext.Array.indexOf(me.pagesRequested, page) === -1 && !me.rangeSatisfied(start, end)) {
            options = options || {};
            me.pagesRequested.push(page);
            Ext.applyIf(options, {
                page : page,
                start: start,
                limit: pageSize,
                callback: me.onWaitForGuarantee,
                scope: me
            });

            me.prefetch(options);
        }

    },

    
    getRequestId: function() {
        this.requestSeed = this.requestSeed || 1;
        return this.requestSeed++;
    },

    
    onProxyPrefetch: function(operation) {
        var me = this,
            resultSet = operation.getResultSet(),
            records = operation.getRecords(),

            successful = operation.wasSuccessful();

        if (resultSet) {
            me.totalCount = resultSet.total;
            me.fireEvent('totalcountchange', me.totalCount);
        }

        if (successful) {
            me.cacheRecords(records, operation);
        }
        Ext.Array.remove(me.pendingRequests, operation.requestId);
        if (operation.page) {
            Ext.Array.remove(me.pagesRequested, operation.page);
        }

        me.loading = false;
        me.fireEvent('prefetch', me, records, successful, operation);

        
        if (operation.blocking) {
            me.fireEvent('load', me, records, successful);
        }

        
        Ext.callback(operation.callback, operation.scope || me, [records, operation, successful]);
    },

    
    cacheRecords: function(records, operation) {
        var me = this,
            i = 0,
            length = records.length,
            start = operation ? operation.start : 0;

        if (!Ext.isDefined(me.totalCount)) {
            me.totalCount = records.length;
            me.fireEvent('totalcountchange', me.totalCount);
        }

        for (; i < length; i++) {
            
            records[i].index = start + i;
        }

        me.prefetchData.addAll(records);
        if (me.purgePageCount) {
            me.purgeRecords();
        }

    },


    
    purgeRecords: function() {
        var me = this,
            prefetchCount = me.prefetchData.getCount(),
            purgeCount = me.purgePageCount * me.pageSize,
            numRecordsToPurge = prefetchCount - purgeCount - 1,
            i = 0;

        for (; i <= numRecordsToPurge; i++) {
            me.prefetchData.removeAt(0);
        }
    },

    
    rangeSatisfied: function(start, end) {
        var me = this,
            i = start,
            satisfied = true;

        for (; i < end; i++) {
            if (!me.prefetchData.getByKey(i)) {
                satisfied = false;
                if (end - i > me.pageSize) {
                    Ext.Error.raise("A single page prefetch could never satisfy this request.");
                }
                break;
            }
        }
        return satisfied;
    },

    
    getPageFromRecordIndex: function(index) {
        return Math.floor(index / this.pageSize) + 1;
    },

    
    onGuaranteedRange: function() {
        var me = this,
            totalCount = me.getTotalCount(),
            start = me.requestStart,
            end = ((totalCount - 1) < me.requestEnd) ? totalCount - 1 : me.requestEnd,
            range = [],
            record,
            i = start;

        end = Math.max(0, end);

        if (start > end) {
            Ext.log({
                level: 'warn',
                msg: 'Start (' + start + ') was greater than end (' + end +
                    ') for the range of records requested (' + me.requestStart + '-' +
                    me.requestEnd + ')' + (this.storeId ? ' from store "' + this.storeId + '"' : '')
            });
        }

        if (start !== me.guaranteedStart && end !== me.guaranteedEnd) {
            me.guaranteedStart = start;
            me.guaranteedEnd = end;

            for (; i <= end; i++) {
                record = me.prefetchData.getByKey(i);



                if (record) {
                    range.push(record);
                }
            }
            me.fireEvent('guaranteedrange', range, start, end);
            if (me.cb) {
                me.cb.call(me.scope || me, range);
            }
        }

        me.unmask();
    },

    
    mask: function() {
        this.masked = true;
        this.fireEvent('beforeload');
    },

    
    unmask: function() {
        if (this.masked) {
            this.fireEvent('load');
        }
    },

    
    hasPendingRequests: function() {
        return this.pendingRequests.length;
    },


    
    onWaitForGuarantee: function() {
        if (!this.hasPendingRequests()) {
            this.onGuaranteedRange();
        }
    },

    
    guaranteeRange: function(start, end, cb, scope) {
        if (start && end) {
            if (end - start > this.pageSize) {
                Ext.Error.raise({
                    start: start,
                    end: end,
                    pageSize: this.pageSize,
                    msg: "Requested a bigger range than the specified pageSize"
                });
            }
        }

        end = (end > this.totalCount) ? this.totalCount - 1 : end;

        var me = this,
            i = start,
            prefetchData = me.prefetchData,
            range = [],
            startLoaded = !!prefetchData.getByKey(start),
            endLoaded = !!prefetchData.getByKey(end),
            startPage = me.getPageFromRecordIndex(start),
            endPage = me.getPageFromRecordIndex(end);

        me.cb = cb;
        me.scope = scope;

        me.requestStart = start;
        me.requestEnd = end;
        
        if (!startLoaded || !endLoaded) {
            
            if (startPage === endPage) {
                me.mask();
                me.prefetchPage(startPage, {
                    
                    callback: me.onWaitForGuarantee,
                    scope: me
                });
                
            } else {
                me.mask();
                me.prefetchPage(startPage, {
                    
                    callback: me.onWaitForGuarantee,
                    scope: me
                });
                me.prefetchPage(endPage, {
                    
                    callback: me.onWaitForGuarantee,
                    scope: me
                });
            }
            
        } else {
            me.onGuaranteedRange();
        }
    },

    
    
    sort: function() {
        var me = this,
            prefetchData = me.prefetchData,
            sorters,
            start,
            end,
            range;

        if (me.buffered) {
            if (me.remoteSort) {
                prefetchData.clear();
                me.callParent(arguments);
            } else {
                sorters = me.getSorters();
                start = me.guaranteedStart;
                end = me.guaranteedEnd;

                if (sorters.length) {
                    prefetchData.sort(sorters);
                    range = prefetchData.getRange();
                    prefetchData.clear();
                    me.cacheRecords(range);
                    delete me.guaranteedStart;
                    delete me.guaranteedEnd;
                    me.guaranteeRange(start, end);
                }
                me.callParent(arguments);
            }
        } else {
            me.callParent(arguments);
        }
    },

    
    
    
    doSort: function(sorterFn) {
        var me = this;
        if (me.remoteSort) {
            
            me.load();
        } else {
            me.data.sortBy(sorterFn);
            if (!me.buffered) {
                var range = me.getRange(),
                    ln = range.length,
                    i = 0;
                for (; i < ln; i++) {
                    range[i].index = i;
                }
            }
            me.fireEvent('sort', me);
            me.fireEvent('datachanged', me);
        }
    },

    
    find: function(property, value, start, anyMatch, caseSensitive, exactMatch) {
        var fn = this.createFilterFn(property, value, anyMatch, caseSensitive, exactMatch);
        return fn ? this.data.findIndexBy(fn, null, start) : -1;
    },

    
    findRecord: function() {
        var me = this,
            index = me.find.apply(me, arguments);
        return index !== -1 ? me.getAt(index) : null;
    },

    
    createFilterFn: function(property, value, anyMatch, caseSensitive, exactMatch) {
        if (Ext.isEmpty(value)) {
            return false;
        }
        value = this.data.createValueMatcher(value, anyMatch, caseSensitive, exactMatch);
        return function(r) {
            return value.test(r.data[property]);
        };
    },

    
    findExact: function(property, value, start) {
        return this.data.findIndexBy(function(rec) {
                return rec.get(property) === value;
            },
            this, start);
    },

    
    findBy: function(fn, scope, start) {
        return this.data.findIndexBy(fn, scope, start);
    },

    
    collect: function(dataIndex, allowNull, bypassFilter) {
        var me = this,
            data = (bypassFilter === true && me.snapshot) ? me.snapshot : me.data;

        return data.collect(dataIndex, 'data', allowNull);
    },

    
    getCount: function() {
        return this.data.length || 0;
    },

    
    getTotalCount: function() {
        return this.totalCount;
    },

    
    getAt: function(index) {
        return this.data.getAt(index);
    },

    
    getRange: function(start, end) {
        return this.data.getRange(start, end);
    },

    
    getById: function(id) {
        return (this.snapshot || this.data).findBy(function(record) {
            return record.getId() === id;
        });
    },

    
    indexOf: function(record) {
        return this.data.indexOf(record);
    },


    
    indexOfTotal: function(record) {
        var index = record.index;
        if (index || index === 0) {
            return index;
        }
        return this.indexOf(record);
    },

    
    indexOfId: function(id) {
        return this.data.indexOfKey(id);
    },

    
    removeAll: function(silent) {
        var me = this;

        me.clearData();
        if (me.snapshot) {
            me.snapshot.clear();
        }
        if (silent !== true) {
            me.fireEvent('clear', me);
        }
    },

    

    
    first: function(grouped) {
        var me = this;

        if (grouped && me.isGrouped()) {
            return me.aggregate(function(records) {
                return records.length ? records[0] : undefined;
            }, me, true);
        } else {
            return me.data.first();
        }
    },

    
    last: function(grouped) {
        var me = this;

        if (grouped && me.isGrouped()) {
            return me.aggregate(function(records) {
                var len = records.length;
                return len ? records[len - 1] : undefined;
            }, me, true);
        } else {
            return me.data.last();
        }
    },

    
    sum: function(field, grouped) {
        var me = this;

        if (grouped && me.isGrouped()) {
            return me.aggregate(me.getSum, me, true, [field]);
        } else {
            return me.getSum(me.data.items, field);
        }
    },

    
    getSum: function(records, field) {
        var total = 0,
            i = 0,
            len = records.length;

        for (; i < len; ++i) {
            total += records[i].get(field);
        }

        return total;
    },

    
    count: function(grouped) {
        var me = this;

        if (grouped && me.isGrouped()) {
            return me.aggregate(function(records) {
                return records.length;
            }, me, true);
        } else {
            return me.getCount();
        }
    },

    
    min: function(field, grouped) {
        var me = this;

        if (grouped && me.isGrouped()) {
            return me.aggregate(me.getMin, me, true, [field]);
        } else {
            return me.getMin(me.data.items, field);
        }
    },

    
    getMin: function(records, field) {
        var i = 1,
            len = records.length,
            value, min;

        if (len > 0) {
            min = records[0].get(field);
        }

        for (; i < len; ++i) {
            value = records[i].get(field);
            if (value < min) {
                min = value;
            }
        }
        return min;
    },

    
    max: function(field, grouped) {
        var me = this;

        if (grouped && me.isGrouped()) {
            return me.aggregate(me.getMax, me, true, [field]);
        } else {
            return me.getMax(me.data.items, field);
        }
    },

    
    getMax: function(records, field) {
        var i = 1,
            len = records.length,
            value,
            max;

        if (len > 0) {
            max = records[0].get(field);
        }

        for (; i < len; ++i) {
            value = records[i].get(field);
            if (value > max) {
                max = value;
            }
        }
        return max;
    },

    
    average: function(field, grouped) {
        var me = this;
        if (grouped && me.isGrouped()) {
            return me.aggregate(me.getAverage, me, true, [field]);
        } else {
            return me.getAverage(me.data.items, field);
        }
    },

    
    getAverage: function(records, field) {
        var i = 0,
            len = records.length,
            sum = 0;

        if (records.length > 0) {
            for (; i < len; ++i) {
                sum += records[i].get(field);
            }
            return sum / len;
        }
        return 0;
    },

    
    aggregate: function(fn, scope, grouped, args) {
        args = args || [];
        if (grouped && this.isGrouped()) {
            var groups = this.getGroups(),
                i = 0,
                len = groups.length,
                out = {},
                group;

            for (; i < len; ++i) {
                group = groups[i];
                out[group.name] = fn.apply(scope || this, [group.children].concat(args));
            }
            return out;
        } else {
            return fn.apply(scope || this, [this.data.items].concat(args));
        }
    }
}, function() {
    
    
    
    Ext.regStore('ext-empty-store', {fields: [], proxy: 'proxy'});
});


Ext.define('Ext.data.ArrayStore', {
    extend: 'Ext.data.Store',
    alias: 'store.array',
    uses: ['Ext.data.reader.Array'],

    constructor: function(config) {
        config = config || {};

        Ext.applyIf(config, {
            proxy: {
                type: 'memory',
                reader: 'array'
            }
        });

        this.callParent([config]);
    },

    loadData: function(data, append) {
        if (this.expandData === true) {
            var r = [],
                i = 0,
                ln = data.length;

            for (; i < ln; i++) {
                r[r.length] = [data[i]];
            }

            data = r;
        }

        this.callParent([data, append]);
    }
}, function() {
    
    Ext.data.SimpleStore = Ext.data.ArrayStore;
    
});


Ext.define('Ext.data.BufferStore', {
    extend: 'Ext.data.Store',
    alias: 'store.buffer',
    sortOnLoad: false,
    filterOnLoad: false,
    
    constructor: function() {
        Ext.Error.raise('The BufferStore class has been deprecated. Instead, specify the buffered config option on Ext.data.Store');
    }
});

Ext.define('Ext.data.JsonPStore', {
    extend: 'Ext.data.Store',
    alias : 'store.jsonp',

    
    constructor: function(config) {
        this.callParent(Ext.apply(config, {
            reader: Ext.create('Ext.data.reader.Json', config),
            proxy : Ext.create('Ext.data.proxy.JsonP', config)
        }));
    }
});


Ext.define('Ext.data.JsonStore',  {
    extend: 'Ext.data.Store',
    alias: 'store.json',

    
    constructor: function(config) {
        config = config || {};

        Ext.applyIf(config, {
            proxy: {
                type  : 'ajax',
                reader: 'json',
                writer: 'json'
            }
        });

        this.callParent([config]);
    }
});


Ext.define('Ext.data.NodeStore', {
    extend: 'Ext.data.Store',
    alias: 'store.node',
    requires: ['Ext.data.NodeInterface'],

    
    node: null,

    
    recursive: false,

    
    rootVisible: false,

    constructor: function(config) {
        var me = this,
            node;

        config = config || {};
        Ext.apply(me, config);

        if (Ext.isDefined(me.proxy)) {
            Ext.Error.raise("A NodeStore cannot be bound to a proxy. Instead bind it to a record " +
                            "decorated with the NodeInterface by setting the node config.");
        }

        config.proxy = {type: 'proxy'};
        me.callParent([config]);

        node = me.node;
        if (node) {
            me.node = null;
            me.setNode(node);
        }
    },

    setNode: function(node) {
        var me = this;

        if (me.node && me.node != node) {
            
            me.mun(me.node, {
                expand: me.onNodeExpand,
                collapse: me.onNodeCollapse,
                append: me.onNodeAppend,
                insert: me.onNodeInsert,
                remove: me.onNodeRemove,
                sort: me.onNodeSort,
                scope: me
            });
            me.node = null;
        }

        if (node !== me.node) {
            Ext.data.NodeInterface.decorate(node);
            me.removeAll();
            if (me.rootVisible) {
                me.add(node);
            }
            else if (!node.isExpanded()) {
                node.expand();
            }

            me.mon(node, {
                expand: me.onNodeExpand,
                collapse: me.onNodeCollapse,
                append: me.onNodeAppend,
                insert: me.onNodeInsert,
                remove: me.onNodeRemove,
                sort: me.onNodeSort,
                scope: me
            });
            me.node = node;
            if (node.isExpanded() && node.isLoaded()) {
                me.onNodeExpand(node, node.childNodes, true);
            }
        }
    },

    onNodeSort: function(node, childNodes) {
        var me = this;

        if ((me.indexOf(node) !== -1 || (node === me.node && !me.rootVisible) && node.isExpanded())) {
            me.onNodeCollapse(node, childNodes, true);
            me.onNodeExpand(node, childNodes, true);
        }
    },

    onNodeExpand: function(parent, records, suppressEvent) {
        var me = this,
            insertIndex = me.indexOf(parent) + 1,
            ln = records ? records.length : 0,
            i, record;

        if (!me.recursive && parent !== me.node) {
            return;
        }

        if (parent !== this.node && !me.isVisible(parent)) {
            return;
        }

        if (!suppressEvent && me.fireEvent('beforeexpand', parent, records, insertIndex) === false) {
            return;
        }

        if (ln) {
            for (i = 0; i < ln; i++) {
                record = records[i];
                
                if (me.data.indexOf(record) === -1) {
                    me.insert(insertIndex, record);
                    insertIndex++;
                }
                if (record.isExpanded()) {
                    if (record.isLoaded()) {
                        
                        me.onNodeExpand(record, record.childNodes, true);
                    }
                    else {
                        record.set('expanded', false);
                        record.expand();
                    }
                }
            }
        }

        if (!suppressEvent) {
            me.fireEvent('expand', parent, records);
        }
    },

    onNodeCollapse: function(parent, records, suppressEvent) {
        var me = this,
            ln = records.length,
            collapseIndex = me.indexOf(parent) + 1,
            i, record;

        if (!me.recursive && parent !== me.node) {
            return;
        }

        if (!suppressEvent && me.fireEvent('beforecollapse', parent, records, collapseIndex) === false) {
            return;
        }

        for (i = 0; i < ln; i++) {
            record = records[i];
            me.remove(record);
            if (record.isExpanded()) {
                me.onNodeCollapse(record, record.childNodes, true);
            }
        }

        if (!suppressEvent) {
            me.fireEvent('collapse', parent, records, collapseIndex);
        }
    },

    onNodeAppend: function(parent, node, index) {
        var me = this,
            refNode, sibling;

        if (me.isVisible(node)) {
            if (index === 0) {
                refNode = parent;
            } else {
                sibling = node.previousSibling;
                while (sibling.isExpanded() && sibling.lastChild) {
                    sibling = sibling.lastChild;
                }
                refNode = sibling;
            }
            me.insert(me.indexOf(refNode) + 1, node);
            if (!node.isLeaf() && node.isExpanded()) {
                if (node.isLoaded()) {
                    
                    me.onNodeExpand(node, node.childNodes, true);
                }
                else {
                    node.set('expanded', false);
                    node.expand();
                }
            }
        }
    },

    onNodeInsert: function(parent, node, refNode) {
        var me = this,
            index = this.indexOf(refNode);

        if (index != -1 && me.isVisible(node)) {
            me.insert(index, node);
            if (!node.isLeaf() && node.isExpanded()) {
                if (node.isLoaded()) {
                    
                    me.onNodeExpand(node, node.childNodes, true);
                }
                else {
                    node.set('expanded', false);
                    node.expand();
                }
            }
        }
    },

    onNodeRemove: function(parent, node, index) {
        var me = this;
        if (me.indexOf(node) != -1) {
            if (!node.isLeaf() && node.isExpanded()) {
                me.onNodeCollapse(node, node.childNodes, true);
            }
            me.remove(node);
        }
    },

    isVisible: function(node) {
        var parent = node.parentNode;
        while (parent) {
            if (parent === this.node && !this.rootVisible && parent.isExpanded()) {
                return true;
            }

            if (this.indexOf(parent) === -1 || !parent.isExpanded()) {
                return false;
            }

            parent = parent.parentNode;
        }
        return true;
    }
});

Ext.define('Ext.data.TreeStore', {
    extend: 'Ext.data.AbstractStore',
    alias: 'store.tree',
    requires: ['Ext.data.Tree', 'Ext.data.NodeInterface', 'Ext.data.NodeStore'],

    

    
    clearOnLoad : true,

    
    nodeParam: 'node',

    
    defaultRootId: 'root',

    
    defaultRootProperty: 'children',

    
    folderSort: false,

    constructor: function(config) {
        var me = this,
            root,
            fields;

        config = Ext.apply({}, config);

        
        fields = config.fields || me.fields;
        if (!fields) {
            config.fields = [
                {name: 'text', type: 'string'}
            ];
        }

        me.callParent([config]);

        
        me.tree = Ext.create('Ext.data.Tree');

        me.relayEvents(me.tree, [
        
            "append",

        
            "remove",

        
            "move",

        
            "insert",

        
            "beforeappend",

        
            "beforeremove",

        
            "beforemove",

        
            "beforeinsert",

        
            "expand",

        
            "collapse",

        
            "beforeexpand",

        
            "beforecollapse",

        
            "rootchange"
        ]);

        me.tree.on({
            scope: me,
            remove: me.onNodeRemove,
            
            
            beforeexpand: me.onBeforeNodeExpand,
            beforecollapse: me.onBeforeNodeCollapse,
            append: me.onNodeAdded,
            insert: me.onNodeAdded
        });

        me.onBeforeSort();

        root = me.root;
        if (root) {
            delete me.root;
            me.setRootNode(root);
        }

        

    },

    
    setProxy: function(proxy) {
        var reader,
            needsRoot;

        if (proxy instanceof Ext.data.proxy.Proxy) {
            
            needsRoot = Ext.isEmpty(proxy.getReader().root);
        } else if (Ext.isString(proxy)) {
            
            needsRoot = true;
        } else {
            
            reader = proxy.reader;
            needsRoot = !(reader && !Ext.isEmpty(reader.root));
        }
        proxy = this.callParent(arguments);
        if (needsRoot) {
            reader = proxy.getReader();
            reader.root = this.defaultRootProperty;
            
            reader.buildExtractors(true);
        }
    },

    
    onBeforeSort: function() {
        if (this.folderSort) {
            this.sort({
                property: 'leaf',
                direction: 'ASC'
            }, 'prepend', false);
        }
    },

    
    onBeforeNodeExpand: function(node, callback, scope) {
        if (node.isLoaded()) {
            Ext.callback(callback, scope || node, [node.childNodes]);
        }
        else if (node.isLoading()) {
            this.on('load', function() {
                Ext.callback(callback, scope || node, [node.childNodes]);
            }, this, {single: true});
        }
        else {
            this.read({
                node: node,
                callback: function() {
                    Ext.callback(callback, scope || node, [node.childNodes]);
                }
            });
        }
    },

    
    getNewRecords: function() {
        return Ext.Array.filter(this.tree.flatten(), this.filterNew);
    },

    
    getUpdatedRecords: function() {
        return Ext.Array.filter(this.tree.flatten(), this.filterUpdated);
    },

    
    onBeforeNodeCollapse: function(node, callback, scope) {
        callback.call(scope || node, node.childNodes);
    },

    onNodeRemove: function(parent, node) {
        var removed = this.removed;

        if (!node.isReplace && Ext.Array.indexOf(removed, node) == -1) {
            removed.push(node);
        }
    },

    onNodeAdded: function(parent, node) {
        var proxy = this.getProxy(),
            reader = proxy.getReader(),
            data = node.raw || node.data,
            dataRoot, children;

        Ext.Array.remove(this.removed, node);

        if (!node.isLeaf() && !node.isLoaded()) {
            dataRoot = reader.getRoot(data);
            if (dataRoot) {
                this.fillNode(node, reader.extractData(dataRoot));
                delete data[reader.root];
            }
        }
    },

    
    setRootNode: function(root) {
        var me = this;

        root = root || {};
        if (!root.isNode) {
            
            Ext.applyIf(root, {
                id: me.defaultRootId,
                text: 'Root',
                allowDrag: false
            });
            root = Ext.ModelManager.create(root, me.model);
        }
        Ext.data.NodeInterface.decorate(root);

        
        
        me.getProxy().getReader().buildExtractors(true);

        
        me.tree.setRootNode(root);

        
        if (!root.isLoaded() && root.isExpanded()) {
            me.load({
                node: root
            });
        }

        return root;
    },

    
    getRootNode: function() {
        return this.tree.getRootNode();
    },

    
    getNodeById: function(id) {
        return this.tree.getNodeById(id);
    },

    
    load: function(options) {
        options = options || {};
        options.params = options.params || {};

        var me = this,
            node = options.node || me.tree.getRootNode(),
            root;

        
        
        if (!node) {
            node = me.setRootNode({
                expanded: true
            });
        }

        if (me.clearOnLoad) {
            node.removeAll();
        }

        Ext.applyIf(options, {
            node: node
        });
        options.params[me.nodeParam] = node ? node.getId() : 'root';

        if (node) {
            node.set('loading', true);
        }

        return me.callParent([options]);
    },


    
    fillNode: function(node, records) {
        var me = this,
            ln = records ? records.length : 0,
            i = 0, sortCollection;

        if (ln && me.sortOnLoad && !me.remoteSort && me.sorters && me.sorters.items) {
            sortCollection = Ext.create('Ext.util.MixedCollection');
            sortCollection.addAll(records);
            sortCollection.sort(me.sorters.items);
            records = sortCollection.items;
        }

        node.set('loaded', true);
        for (; i < ln; i++) {
            node.appendChild(records[i], undefined, true);
        }

        return records;
    },

    
    onProxyLoad: function(operation) {
        var me = this,
            successful = operation.wasSuccessful(),
            records = operation.getRecords(),
            node = operation.node;

        me.loading = false;
        node.set('loading', false);
        if (successful) {
            records = me.fillNode(node, records);
        }
        
        
        
        
        me.fireEvent('read', me, operation.node, records, successful);
        me.fireEvent('load', me, operation.node, records, successful);
        
        Ext.callback(operation.callback, operation.scope || me, [records, operation, successful]);
    },

    
    onCreateRecords: function(records, operation, success) {
        if (success) {
            var i = 0,
                length = records.length,
                originalRecords = operation.records,
                parentNode,
                record,
                original,
                index;

            
            for (; i < length; ++i) {
                record = records[i];
                original = originalRecords[i];
                if (original) {
                    parentNode = original.parentNode;
                    if (parentNode) {
                        
                        original.isReplace = true;
                        parentNode.replaceChild(record, original);
                        delete original.isReplace;
                    }
                    record.phantom = false;
                }
            }
        }
    },

    
    onUpdateRecords: function(records, operation, success) {
        if (success) {
            var me = this,
                i = 0,
                length = records.length,
                data = me.data,
                original,
                parentNode,
                record;

            for (; i < length; ++i) {
                record = records[i];
                original = me.tree.getNodeById(record.getId());
                parentNode = original.parentNode;
                if (parentNode) {
                    
                    original.isReplace = true;
                    parentNode.replaceChild(record, original);
                    original.isReplace = false;
                }
            }
        }
    },

    
    onDestroyRecords: function(records, operation, success) {
        if (success) {
            this.removed = [];
        }
    },

    
    removeAll: function() {
        this.getRootNode().destroy(true);
        this.fireEvent('clear', this);
    },

    
    doSort: function(sorterFn) {
        var me = this;
        if (me.remoteSort) {
            
            me.load();
        } else {
            me.tree.sort(sorterFn, true);
            me.fireEvent('datachanged', me);
        }
        me.fireEvent('sort', me);
    }
});

Ext.define('Ext.data.XmlStore', {
    extend: 'Ext.data.Store',
    alias: 'store.xml',

    
    constructor: function(config){
        config = config || {};
        config = config || {};

        Ext.applyIf(config, {
            proxy: {
                type: 'ajax',
                reader: 'xml',
                writer: 'xml'
            }
        });

        this.callParent([config]);
    }
});


Ext.define('Ext.data.proxy.Rest', {
    extend: 'Ext.data.proxy.Ajax',
    alternateClassName: 'Ext.data.RestProxy',
    alias : 'proxy.rest',
    
    
    appendId: true,
    
    
    
    
    batchActions: false,
    
    
    buildUrl: function(request) {
        var me        = this,
            operation = request.operation,
            records   = operation.records || [],
            record    = records[0],
            format    = me.format,
            url       = me.getUrl(request),
            id        = record ? record.getId() : operation.id;
        
        if (me.appendId && id) {
            if (!url.match(/\/$/)) {
                url += '/';
            }
            
            url += id;
        }
        
        if (format) {
            if (!url.match(/\.$/)) {
                url += '.';
            }
            
            url += format;
        }
        
        request.url = url;
        
        return me.callParent(arguments);
    }
}, function() {
    Ext.apply(this.prototype, {
        
        actionMethods: {
            create : 'POST',
            read   : 'GET',
            update : 'PUT',
            destroy: 'DELETE'
        }
    });
});


Ext.define('Ext.direct.Manager', {

    
    singleton: true,

    mixins: {
        observable: 'Ext.util.Observable'
    },

    requires: ['Ext.util.MixedCollection'],

    statics: {
        exceptions: {
            TRANSPORT: 'xhr',
            PARSE: 'parse',
            LOGIN: 'login',
            SERVER: 'exception'
        }
    },

    

    constructor: function(){
        var me = this;

        me.addEvents(
            
            'event',
            
            'exception'
        );
        me.transactions = Ext.create('Ext.util.MixedCollection');
        me.providers = Ext.create('Ext.util.MixedCollection');

        me.mixins.observable.constructor.call(me);
    },

    
    addProvider : function(provider){
        var me = this,
            args = arguments,
            i = 0,
            len;

        if (args.length > 1) {
            for (len = args.length; i < len; ++i) {
                me.addProvider(args[i]);
            }
            return;
        }

        
        if (!provider.isProvider) {
            provider = Ext.create('direct.' + provider.type + 'provider', provider);
        }
        me.providers.add(provider);
        provider.on('data', me.onProviderData, me);


        if (!provider.isConnected()) {
            provider.connect();
        }

        return provider;
    },

    
    getProvider : function(id){
        return id.isProvider ? id : this.providers.get(id);
    },

    
    removeProvider : function(provider){
        var me = this,
            providers = me.providers;

        provider = provider.isProvider ? provider : providers.get(provider);

        if (provider) {
            provider.un('data', me.onProviderData, me);
            providers.remove(provider);
            return provider;
        }
        return null;
    },

    
    addTransaction: function(transaction){
        this.transactions.add(transaction);
        return transaction;
    },

    
    removeTransaction: function(transaction){
        transaction = this.getTransaction(transaction);
        this.transactions.remove(transaction);
        return transaction;
    },

    
    getTransaction: function(transaction){
        return transaction.isTransaction ? transaction : this.transactions.get(transaction);
    },

    onProviderData : function(provider, event){
        var me = this,
            i = 0,
            len;

        if (Ext.isArray(event)) {
            for (len = event.length; i < len; ++i) {
                me.onProviderData(provider, event[i]);
            }
            return;
        }
        if (event.name && event.name != 'event' && event.name != 'exception') {
            me.fireEvent(event.name, event);
        } else if (event.status === false) {
            me.fireEvent('exception', event);
        }
        me.fireEvent('event', event, provider);
    }
}, function(){
    
    Ext.Direct = Ext.direct.Manager;
});


Ext.define('Ext.data.proxy.Direct', {
    

    extend: 'Ext.data.proxy.Server',
    alternateClassName: 'Ext.data.DirectProxy',

    alias: 'proxy.direct',

    requires: ['Ext.direct.Manager'],

    

    
    paramOrder: undefined,

    
    paramsAsHash: true,

    
    directFn : undefined,

    

    

    
    paramOrderRe: /[\s,|]/,

    constructor: function(config){
        var me = this;

        Ext.apply(me, config);
        if (Ext.isString(me.paramOrder)) {
            me.paramOrder = me.paramOrder.split(me.paramOrderRe);
        }
        me.callParent(arguments);
    },

    doRequest: function(operation, callback, scope) {
        var me = this,
            writer = me.getWriter(),
            request = me.buildRequest(operation, callback, scope),
            fn = me.api[request.action]  || me.directFn,
            args = [],
            params = request.params,
            paramOrder = me.paramOrder,
            method,
            i = 0,
            len;

        if (!fn) {
            Ext.Error.raise('No direct function specified for this proxy');
        }

        if (operation.allowWrite()) {
            request = writer.write(request);
        }

        if (operation.action == 'read') {
            
            method = fn.directCfg.method;

            if (method.ordered) {
                if (method.len > 0) {
                    if (paramOrder) {
                        for (len = paramOrder.length; i < len; ++i) {
                            args.push(params[paramOrder[i]]);
                        }
                    } else if (me.paramsAsHash) {
                        args.push(params);
                    }
                }
            } else {
                args.push(params);
            }
        } else {
            args.push(request.jsonData);
        }

        Ext.apply(request, {
            args: args,
            directFn: fn
        });
        args.push(me.createRequestCallback(request, operation, callback, scope), me);
        fn.apply(window, args);
    },

    
    applyEncoding: function(value){
        return value;
    },

    createRequestCallback: function(request, operation, callback, scope){
        var me = this;

        return function(data, event){
            me.processResponse(event.status, operation, request, event, callback, scope);
        };
    },

    
    extractResponseData: function(response){
        return Ext.isDefined(response.result) ? response.result : response.data;
    },

    
    setException: function(operation, response) {
        operation.setException(response.message);
    },

    
    buildUrl: function(){
        return '';
    }
});


Ext.define('Ext.data.DirectStore', {
    
    
    extend: 'Ext.data.Store',
    
    alias: 'store.direct',
    
    requires: ['Ext.data.proxy.Direct'],
   
    

    constructor : function(config){
        config = Ext.apply({}, config);
        if (!config.proxy) {
            var proxy = {
                type: 'direct',
                reader: {
                    type: 'json'
                }
            };
            Ext.copyTo(proxy, config, 'paramOrder,paramsAsHash,directFn,api,simpleSortMode');
            Ext.copyTo(proxy.reader, config, 'totalProperty,root,idProperty');
            config.proxy = proxy;
        }
        this.callParent([config]);
    }    
});


Ext.define('Ext.direct.Event', {
    
    
   
    alias: 'direct.event',
    
    requires: ['Ext.direct.Manager'],
    
    
   
    status: true,

    
    constructor: function(config) {
        Ext.apply(this, config);
    },
    
    
    getData: function(){
        return this.data;
    }
});


Ext.define('Ext.direct.RemotingEvent', {
    
    
   
    extend: 'Ext.direct.Event',
    
    alias: 'direct.rpc',
    
    
    
    
    getTransaction: function(){
        return this.transaction || Ext.direct.Manager.getTransaction(this.tid);
    }
});


Ext.define('Ext.direct.ExceptionEvent', {
    
    
   
    extend: 'Ext.direct.RemotingEvent',
    
    alias: 'direct.exception',
    
    
   
   status: false
});


Ext.define('Ext.direct.RemotingProvider', {
    
    
   
    alias: 'direct.remotingprovider',
    
    extend: 'Ext.direct.JsonProvider', 
    
    requires: [
        'Ext.util.MixedCollection', 
        'Ext.util.DelayedTask', 
        'Ext.direct.Transaction',
        'Ext.direct.RemotingMethod'
    ],
   
    
   
   
    
    
    
    
    
    
    
    
    enableBuffer: 10,
    
    
    maxRetries: 1,
    
    
    timeout: undefined,
    
    constructor : function(config){
        var me = this;
        me.callParent(arguments);
        me.addEvents(
                        
            'beforecall',            
                        
            'call'
        );
        me.namespace = (Ext.isString(me.namespace)) ? Ext.ns(me.namespace) : me.namespace || window;
        me.transactions = Ext.create('Ext.util.MixedCollection');
        me.callBuffer = [];
    },
    
    
    initAPI : function(){
        var actions = this.actions,
            namespace = this.namespace,
            action,
            cls,
            methods,
            i,
            len,
            method;
            
        for (action in actions) {
            cls = namespace[action];
            if (!cls) {
                cls = namespace[action] = {};
            }
            methods = actions[action];
            
            for (i = 0, len = methods.length; i < len; ++i) {
                method = Ext.create('Ext.direct.RemotingMethod', methods[i]);
                cls[method.name] = this.createHandler(action, method);
            }
        }
    },
    
    
    createHandler : function(action, method){
        var me = this,
            handler;
        
        if (!method.formHandler) {
            handler = function(){
                me.configureRequest(action, method, Array.prototype.slice.call(arguments, 0));
            };
        } else {
            handler = function(form, callback, scope){
                me.configureFormRequest(action, method, form, callback, scope);
            };
        }
        handler.directCfg = {
            action: action,
            method: method
        };
        return handler;
    },
    
    
    isConnected: function(){
        return !!this.connected;
    },

    
    connect: function(){
        var me = this;
        
        if (me.url) {
            me.initAPI();
            me.connected = true;
            me.fireEvent('connect', me);
        } else if(!me.url) {
            Ext.Error.raise('Error initializing RemotingProvider, no url configured.');
        }
    },

    
    disconnect: function(){
        var me = this;
        
        if (me.connected) {
            me.connected = false;
            me.fireEvent('disconnect', me);
        }
    },
    
    
    runCallback: function(transaction, event){
        var funcName = event.status ? 'success' : 'failure',
            callback,
            result;
        
        if (transaction && transaction.callback) {
            callback = transaction.callback;
            result = Ext.isDefined(event.result) ? event.result : event.data;
        
            if (Ext.isFunction(callback)) {
                callback(result, event);
            } else {
                Ext.callback(callback[funcName], callback.scope, [result, event]);
                Ext.callback(callback.callback, callback.scope, [result, event]);
            }
        }
    },
    
    
    onData: function(options, success, response){
        var me = this,
            i = 0,
            len,
            events,
            event,
            transaction,
            transactions;
            
        if (success) {
            events = me.createEvents(response);
            for (len = events.length; i < len; ++i) {
                event = events[i];
                transaction = me.getTransaction(event);
                me.fireEvent('data', me, event);
                if (transaction) {
                    me.runCallback(transaction, event, true);
                    Ext.direct.Manager.removeTransaction(transaction);
                }
            }
        } else {
            transactions = [].concat(options.transaction);
            for (len = transactions.length; i < len; ++i) {
                transaction = me.getTransaction(transactions[i]);
                if (transaction && transaction.retryCount < me.maxRetries) {
                    transaction.retry();
                } else {
                    event = Ext.create('Ext.direct.ExceptionEvent', {
                        data: null,
                        transaction: transaction,
                        code: Ext.direct.Manager.self.exceptions.TRANSPORT,
                        message: 'Unable to connect to the server.',
                        xhr: response
                    });
                    me.fireEvent('data', me, event);
                    if (transaction) {
                        me.runCallback(transaction, event, false);
                        Ext.direct.Manager.removeTransaction(transaction);
                    }
                }
            }
        }
    },
    
    
    getTransaction: function(options){
        return options && options.tid ? Ext.direct.Manager.getTransaction(options.tid) : null;
    },
    
    
    configureRequest: function(action, method, args){
        var me = this,
            callData = method.getCallData(args),
            data = callData.data, 
            callback = callData.callback, 
            scope = callData.scope,
            transaction;

        transaction = Ext.create('Ext.direct.Transaction', {
            provider: me,
            args: args,
            action: action,
            method: method.name,
            data: data,
            callback: scope && Ext.isFunction(callback) ? Ext.Function.bind(callback, scope) : callback
        });

        if (me.fireEvent('beforecall', me, transaction, method) !== false) {
            Ext.direct.Manager.addTransaction(transaction);
            me.queueTransaction(transaction);
            me.fireEvent('call', me, transaction, method);
        }
    },
    
    
    getCallData: function(transaction){
        return {
            action: transaction.action,
            method: transaction.method,
            data: transaction.data,
            type: 'rpc',
            tid: transaction.id
        };
    },
    
    
    sendRequest : function(data){
        var me = this,
            request = {
                url: me.url,
                callback: me.onData,
                scope: me,
                transaction: data,
                timeout: me.timeout
            }, callData,
            enableUrlEncode = me.enableUrlEncode,
            i = 0,
            len,
            params;
            

        if (Ext.isArray(data)) {
            callData = [];
            for (len = data.length; i < len; ++i) {
                callData.push(me.getCallData(data[i]));
            }
        } else {
            callData = me.getCallData(data);
        }

        if (enableUrlEncode) {
            params = {};
            params[Ext.isString(enableUrlEncode) ? enableUrlEncode : 'data'] = Ext.encode(callData);
            request.params = params;
        } else {
            request.jsonData = callData;
        }
        Ext.Ajax.request(request);
    },
    
    
    queueTransaction: function(transaction){
        var me = this,
            enableBuffer = me.enableBuffer;
        
        if (transaction.form) {
            me.sendFormRequest(transaction);
            return;
        }
        
        me.callBuffer.push(transaction);
        if (enableBuffer) {
            if (!me.callTask) {
                me.callTask = Ext.create('Ext.util.DelayedTask', me.combineAndSend, me);
            }
            me.callTask.delay(Ext.isNumber(enableBuffer) ? enableBuffer : 10);
        } else {
            me.combineAndSend();
        }
    },
    
    
    combineAndSend : function(){
        var buffer = this.callBuffer,
            len = buffer.length;
            
        if (len > 0) {
            this.sendRequest(len == 1 ? buffer[0] : buffer);
            this.callBuffer = [];
        }
    },
    
    
    configureFormRequest : function(action, method, form, callback, scope){
        var me = this,
            transaction = Ext.create('Ext.direct.Transaction', {
                provider: me,
                action: action,
                method: method.name,
                args: [form, callback, scope],
                callback: scope && Ext.isFunction(callback) ? Ext.Function.bind(callback, scope) : callback,
                isForm: true
            }),
            isUpload,
            params;

        if (me.fireEvent('beforecall', me, transaction, method) !== false) {
            Ext.direct.Manager.addTransaction(transaction);
            isUpload = String(form.getAttribute("enctype")).toLowerCase() == 'multipart/form-data';
            
            params = {
                extTID: transaction.id,
                extAction: action,
                extMethod: method.name,
                extType: 'rpc',
                extUpload: String(isUpload)
            };
            
            
            
            Ext.apply(transaction, {
                form: Ext.getDom(form),
                isUpload: isUpload,
                params: callback && Ext.isObject(callback.params) ? Ext.apply(params, callback.params) : params
            });
            me.fireEvent('call', me, transaction, method);
            me.sendFormRequest(transaction);
        }
    },
    
    
    sendFormRequest: function(transaction){
        Ext.Ajax.request({
            url: this.url,
            params: transaction.params,
            callback: this.onData,
            scope: this,
            form: transaction.form,
            isUpload: transaction.isUpload,
            transaction: transaction
        });
    }
    
});


Ext.define('Ext.EventedBase', {

    mixins: ['Ext.mixin.Observable'],

    initialized: false,

    isInitialized: function() {
        return this.initialized;
    },

    constructor: function(config) {
        this.initConfig(config);

        this.initialized = true;

        this.initialize();
    },

    initialize: Ext.emptyFn,

    doSet: function(me, value, oldValue, options) {
        var nameMap = options.nameMap;

        me[nameMap.internal] = value;
        me[nameMap.doSet](value, oldValue);
    },

    onClassExtended: function(Class, data) {
        if (!data.hasOwnProperty('eventedConfig')) {
            return;
        }

        var ExtClass = Ext.Class,
            config = data.config,
            eventedConfig = data.eventedConfig;

        data.config = (config) ? Ext.applyIf(config, eventedConfig) : eventedConfig;

        Ext.Object.each(eventedConfig, function(name) {
            var map = ExtClass.getConfigNameMap(name),
                internalName = map.internal,
                doSetName = map.doSet,
                applyName = map.apply,
                options = {
                    nameMap: map
                },
                changeEventName = map.changeEvent;

            
            Class.addMember(map.set, function(value) {
                var initialized = this.initialized,
                    oldValue = this[internalName],
                    applier = this[applyName];

                if (applier) {
                    value = applier.call(this, value, oldValue);

                    if (typeof value == 'undefined') {
                        return this;
                    }
                }

                if (value !== oldValue) {
                    if (initialized) {
                        this.fireAction(changeEventName, [this, value, oldValue], this.doSet, this, options);
                    }
                    else {
                        this[internalName] = value;
                        this[doSetName](value, oldValue);
                    }
                }

                return this;
            });
        });
    }
});

Ext.define('Ext.ItemCollection', {
    extend: 'Ext.util.MixedCollection',

    getKey: function(item) {
        return item.getItemId();
    },

    has: function(item) {
        return this.map.hasOwnProperty(item.getId());
    }
});


Ext.define('Ext.LoadMask', {
    requires: ['Ext.data.StoreManager'],

    mixins: {
        observable: 'Ext.util.Observable'
    },

    config: {
        
        msg: 'Loading...',

        
        msgCls: Ext.baseCSSPrefix + 'mask-loading'
    },

    

    
    disabled: false,

    
    applyMsg: function(msg) {
        if (this.el) {
            var me = this,
                dom = me.el.dom,
                mask = Ext.Element.data(dom, 'mask');

            
            if (mask) {
                var maskEl = el.child('.x-loading-msg');
                if (maskEl) {
                    maskEl.update(msg);
                }
            }
        }

        return msg;
    },

    
    applyMsgCls: function(msgCls) {
        if (this.el) {
            var me = this,
                dom = me.el.dom,
                mask = Ext.Element.data(dom, 'mask');

            
            if (mask) {
                
                
                
                
                
            }
        }

        return msgCls;
    },

    
    constructor: function(el, config) {
        var me = this;

        me.el = Ext.get(el);
        Ext.apply(me, config);

        me.addEvents('show', 'hide');
        if (me.store) {
            me.bindStore(me.store, true);
        }

        me.callParent();

        me.mixins.observable.constructor.call(me);
    },

    
    bindStore: function(store, initial) {
        if (!initial && this.store) {
            this.mun(this.store, {
                scope: this,
                beforeload: this.onBeforeLoad,
                load: this.onLoad,
                exception: this.onLoad
            });

            if (!store) {
                this.store = null;
            }
        }

        if (store) {
            store = Ext.StoreMgr.lookup(store);
            this.mon(store, {
                scope: this,
                beforeload: this.onBeforeLoad,
                load: this.onLoad,
                exception: this.onLoad
            });
        }

        this.store = store;
        if (store && store.isLoading()) {
            this.onBeforeLoad();
        }
    },

    
    disable: function() {
       this.setDisabled(true);
    },

    
    enable: function() {
        this.setDisabled(false);
    },

    
    isDisabled: function() {
        return this.getDisabled();
    },

    
    onLoad: function() {
        this.el.unmask();
        this.fireEvent('hide', this, this.el, this.store);
    },

    
    onBeforeLoad: function() {
        if (!this.disabled) {
            this.el.mask(Ext.LoadingSpinner + '<div class="x-loading-msg">' + this.msg + '</div>', this.msgCls, false);
            this.fireEvent('show', this, this.el, this.store);
        }
    },

    
    show: function() {
        this.onBeforeLoad();
    },

    
    hide: function() {
        this.onLoad();
    },

    
    destroy: function() {
        this.hide();
        this.clearListeners();
    }
}, function() {
    Ext.LoadingSpinner = '<div class="x-loading-spinner"><span class="x-loading-top"></span><span class="x-loading-right"></span><span class="x-loading-bottom"></span><span class="x-loading-left"></span></div>';
});



Ext.define('Ext.app.Controller', {
    alternateClassName: 'Ext.Application',
    
    mixins: {
        observable: 'Ext.util.Observable'
    },

    

    onClassExtended: function(cls, data, hooks) {
        var className = Ext.getClassName(cls),
            match = className.match(/^(.*)\.controller\./);

        if (match !== null) {
            var namespace = Ext.Loader.getPrefix(className) || match[1],
                onBeforeClassCreated = hooks.onBeforeCreated,
                requires = [],
                modules = ['model', 'view', 'store'],
                prefix;

            hooks.onBeforeCreated = function(cls, data) {
                var i, ln, module,
                    items, j, subLn, item;

                for (i = 0,ln = modules.length; i < ln; i++) {
                    module = modules[i];

                    items = Ext.Array.from(data[module + 's']);

                    for (j = 0,subLn = items.length; j < subLn; j++) {
                        item = items[j];

                        prefix = Ext.Loader.getPrefix(item);

                        if (prefix === '' || prefix === item) {
                            requires.push(namespace + '.' + module + '.' + item);
                        }
                        else {
                            requires.push(item);
                        }
                    }
                }

                Ext.require(requires, Ext.Function.pass(onBeforeClassCreated, arguments, this));
            };
        }
    },

    
    constructor: function(config) {
        this.mixins.observable.constructor.call(this, config);

        Ext.apply(this, config || {});
        
        this.createGetters('model', this.models);
        this.createGetters('store', this.stores);
        this.createGetters('view', this.views);

        if (this.refs) {
            this.ref(this.refs);
        }

        this.initialConfig = config;
    },

    
    init: function(application) {},

    
    launch: function(application) {},

    createGetters: function(type, refs) {
        type = Ext.String.capitalize(type);
        Ext.Array.each(refs, function(ref) {
            var fn = 'get',
                parts = ref.split('.');

            
            Ext.Array.each(parts, function(part) {
                fn += Ext.String.capitalize(part);
            });
            fn += type;

            if (!this[fn]) {
                this[fn] = Ext.Function.pass(this['get' + type], [ref], this);
            }
            
            this[fn](ref);
        },
        this);
    },

    ref: function(refs) {
        var me = this;
        refs = Ext.Array.from(refs);
        Ext.Array.each(refs, function(info) {
            var ref = info.ref,
                fn = 'get' + Ext.String.capitalize(ref);
            if (!me[fn]) {
                me[fn] = Ext.Function.pass(me.getRef, [ref, info], me);
            }
            me.references = me.references || [];
            me.references.push(ref.toLowerCase());
        });
    },

    addRef: function(ref) {
        return this.ref([ref]);
    },

    getRef: function(ref, info, config) {
        this.refCache = this.refCache || {};
        info = info || {};
        config = config || {};

        Ext.apply(info, config);

        if (info.forceCreate) {
            return Ext.ComponentManager.create(info, 'component');
        }

        var me = this,
            selector = info.selector,
            cached = me.refCache[ref];

        if (!cached) {
            me.refCache[ref] = cached = Ext.ComponentQuery.query(info.selector)[0];
            if (!cached && info.autoCreate) {
                me.refCache[ref] = cached = Ext.ComponentManager.create(info, 'component');
            }
            if (cached) {
                cached.on('beforedestroy', function() {
                    me.refCache[ref] = null;
                });
            }
        }

        return cached;
    },

    hasRef: function(ref) {
        return this.references && this.references.indexOf(ref.toLowerCase()) !== -1;
    },

    
    control: function(selectors, listeners) {
        this.application.control(selectors, listeners, this);
    },

    
    getController: function(name) {
        return this.application.getController(name);
    },

    
    getStore: function(name) {
        return this.application.getStore(name);
    },

    
    getModel: function(model) {
        return this.application.getModel(model);
    },

    
    getView: function(view) {
        return this.application.getView(view);
    }
});


Ext.define('Ext.app.Application', {
    extend: 'Ext.app.Controller',
    alternateClassName: 'Ext.Application',
    
    requires: [
        'Ext.ModelManager',
        'Ext.data.Model',
        'Ext.data.StoreManager',
        'Ext.ComponentManager'
    ],

    

    
    scope: undefined,

    
    enableQuickTips: true,

    

    
    appFolder: 'app',

    
    autoCreateViewport: false,

    
    constructor: function(config) {
        config = config || {};
        Ext.apply(this, config);

        var requires = config.requires || [],
            name = this.name;
        
        Ext.Loader.setPath(name, this.appFolder);

        if (this.paths) {
            Ext.Object.each(this.paths, function(key, value) {
                Ext.Loader.setPath(key, value);
            });
        }

        this.callParent(arguments);

        var controllers = Ext.Array.from(this.controllers),
            ln = controllers && controllers.length,
            i, controller;

        this.controllers = controllers;

        if (this.autoCreateViewport) {
            requires.push(this.getModuleClassName('Viewport', 'view'));
        }

        for (i = 0; i < ln; i++) {
            requires.push(this.getModuleClassName(controllers[i], 'controller'));
        }

        Ext.require(requires);

        Ext.onReady(this.onBeforeLaunch, this);
    },


    control: function(selectors, listeners, controller) {
        var dispatcher = this.getEventDispatcher(),
            selector, eventName, listener;

        for (selector in selectors) {
            if (selectors.hasOwnProperty(selector)) {
                listeners = selectors[selector];

                for (eventName in listeners) {
                    if (listeners.hasOwnProperty(eventName)) {
                        listener = listeners[eventName];

                        dispatcher.addListener('component', selector, eventName, listener, controller);
                    }
                }
            }
        }
    },


    
    init: Ext.emptyFn,

    
    launch: Ext.emptyFn,

    
    onBeforeLaunch: function() {
        if (this.autoCreateViewport) {
            this.getView('Viewport').create();
        }

        var controllers = this.controllers,
            ln = controllers.length,
            i, controller;

        this.controllers = Ext.create('Ext.util.MixedCollection');

        this.init();

        for (i = 0; i < ln; i++) {
            controller = this.getController(controllers[i], false);
            controller.initConfig(controller.initialConfig);
            controller.init();
        }

        this.launch.call(this.scope || this);

        this.controllers.each(function(controller) {
            
            if (controller.onLaunch) {
                controller.onLaunch(this);
            } else {
                controller.launch(this);
            }
        }, this);

        this.launched = true;
        this.fireEvent('launch', this);
    },

    getModuleClassName: function(name, type) {
        var namespace = Ext.Loader.getPrefix(name);

        if (namespace.length > 0 && namespace !== name) {
            return name;
        }

        return this.name + '.' + type + '.' + name;
    },

    getController: function(name, autoInit) {
        var controller = this.controllers.get(name);

        if (!controller) {
            controller = Ext.create(this.getModuleClassName(name, 'controller'), {
                application: this,
                id: name
            });

            this.controllers.add(controller);
                if (autoInit !== false) {
                controller.init();
                    
                if (this.launched) {
                    if (controller.onLaunch) {
                        controller.onLaunch(this);
                    } else {
                        controller.launch(this);
                    }
                }
            }
        }

        return controller;
    },

    getStore: function(name) {
        var store = Ext.StoreManager.get(name);

        if (!store) {
            store = Ext.create(this.getModuleClassName(name, 'store'), {
                storeId: name
            });
        }

        return store;
    },

    getModel: function(model) {
        model = this.getModuleClassName(model, 'model');

        return Ext.ModelManager.getModel(model);
    },

    getView: function(view) {
        var viewClassName = this.getModuleClassName(view, 'view'),
            viewCls = Ext.ClassManager.get(viewClassName),
            xtype = view.toLowerCase() + 'view';

        if (viewCls) {
            viewCls.addXtype(view.toLowerCase() + 'view');
        }
        else {
            Ext.ClassManager.setAlias(viewClassName, 'widget.' + xtype);
        }

        return viewCls;
    },

    createViewInstance: function(view) {
        return this.getView(view).create();
    }
});


Ext.define('Ext.dom.Element', {
    extend: 'Ext.dom.AbstractElement',
    alternateClassName: 'Ext.Element',

    requires: [
        'Ext.dom.Query',
        'Ext.dom.Helper'
    ],

    mixins: [
        'Ext.mixin.Observable'
    ],

    observableType: 'element',

    xtype: 'element',

    WIDTH: 'width',

    HEIGHT: 'height',

    TOP: 'top',

    RIGHT: 'right',

    BOTTOM: 'bottom',

    LEFT: 'left',

    SEPARATOR: '-',

    spacesRegex: /\s+/,

    statics: {
        CREATE_ATTRIBUTES: {
            style: 'style',
            className: 'className',
            cls: 'cls',
            classList: 'classList',
            text: 'text',
            hidden: 'hidden',
            html: 'html',
            children: 'children'
        },

        create: function(attributes, domNode) {
            var ATTRIBUTES = this.CREATE_ATTRIBUTES,
                element, elementStyle, tag, value, name, i, ln;

            if (!attributes) {
                attributes = {};
            }

            if (attributes.isElement) {
                return attributes.dom;
            }
            else if ('nodeType' in attributes) {
                return attributes;
            }

            if (typeof attributes == 'string') {
                return document.createTextNode(attributes);
            }

            tag = attributes.tag;

            if (!tag) {
                tag = 'div';
            }

            element = document.createElement(tag);
            elementStyle = element.style;

            for (name in attributes) {
                if (name != 'tag' && attributes.hasOwnProperty(name)) {
                    value = attributes[name];

                    switch (name) {
                        case ATTRIBUTES.style:
                                if (typeof value == 'string') {
                                    element.setAttribute(name, value);
                                }
                                else {
                                    for (i in value) {
                                        if (value.hasOwnProperty(i)) {
                                            elementStyle[i] = value[i];
                                        }
                                    }
                                }
                            break;

                        case ATTRIBUTES.className:
                        case ATTRIBUTES.cls:
                            element.className = value;
                            break;

                        case ATTRIBUTES.classList:
                            element.className = value.join(' ');
                            break;

                        case ATTRIBUTES.text:
                            element.textContent = value;
                            break;

                        case ATTRIBUTES.hidden:
                            if (value) {
                                element.style.display = 'none';
                            }
                            break;

                        case ATTRIBUTES.html:
                            element.innerHTML = value;
                            break;

                        case ATTRIBUTES.children:
                            for (i = 0,ln = value.length; i < ln; i++) {
                                element.appendChild(this.create(value[i], true));
                            }
                            break;

                        default:
                            element.setAttribute(name, value);
                    }
                }
            }

            if (domNode) {
                return element;
            }
            else {
                return Ext.get(element);
            }
        },

        documentElement: null,

        cache: {},

        get: function(element) {
            var cache = this.cache,
                instance, dom, id;

            if (!element) {
                return null;
            }

            if (typeof element == 'string') {
                if (cache.hasOwnProperty(element)) {
                    return cache[element];
                }

                if (!(dom = document.getElementById(element))) {
                    return null;
                }

                cache[element] = instance = new this(dom);

                return instance;
            }

            if ('tagName' in element) { 
                id = element.id;

                if (cache.hasOwnProperty(id)) {
                    return cache[id];
                }

                instance = new this(element);
                cache[instance.getId()] = instance;

                return instance;
            }

            if (element.isElement) {
                return element;
            }

            if (element.isComposite) {
                return element;
            }

            if (Ext.isArray(element)) {
                return this.select(element);
            }

            if (element === document) {
                
                if (!this.documentElement) {
                    this.documentElement = new this(document.documentElement);
                    this.documentElement.setId('ext-application');
                }

                return this.documentElement;
            }

            return null;
        }
    },

    isElement: true,

    classNameSplitRegex: /[\s]+/,

    isSynchronized: false,

    constructor: function(dom) {
        if (typeof dom == 'string') {
            dom = document.getElementById(dom);
        }

        if (!dom) {
            throw new Error("Invalid domNode reference or an id of an existing domNode: " + dom);
        }

        this.dom = dom;

        this.getUniqueId();
    },

    getUniqueId: function() {
        var id = this.id,
            dom;

        if (!id) {
            dom = this.dom;

            if (dom.id.length > 0) {
                this.id = id = dom.id;
            }
            else {
                dom.id = id = this.mixins.identifiable.getUniqueId.call(this);
            }

            Ext.Element.cache[id] = this;
        }

        return id;
    },

    setId: function(id) {
        var currentId = this.id,
            cache = Ext.Element.cache;

        if (currentId) {
            delete cache[currentId];
        }

        this.dom.id = id;
        this.id = id;

        cache[id] = this;

        return this;
    },

    
    synchronize: function() {
        var dom = this.dom,
            hasClassMap = {},
            className = dom.className,
            classList, i, ln, name;

        if (className.length > 0) {
            classList = dom.className.split(this.classNameSplitRegex);

            for (i = 0,ln = classList.length; i < ln; i++) {
                name = classList[i];
                hasClassMap[name] = true;
            }
        }
        else {
            classList = [];
        }

        this.classList = classList;

        this.hasClassMap = hasClassMap;

        this.isSynchronized = true;

        return this;
    },

    
    addCls: function(names, prefix, suffix) {
        if (!names) {
            return this;
        }

        if (!this.isSynchronized) {
            this.synchronize();
        }

        var dom = this.dom,
            map = this.hasClassMap,
            classList = this.classList,
            SEPARATOR = this.SEPARATOR,
            i, ln, name;

        prefix = prefix ? prefix + SEPARATOR : '';
        suffix = suffix ? SEPARATOR + suffix : '';

        if (typeof names == 'string') {
            names = names.split(this.spacesRegex);
        }

        for (i = 0,ln = names.length; i < ln; i++) {
            name = prefix + names[i] + suffix;

            if (!map[name]) {
                map[name] = true;
                classList.push(name);
            }
        }

        dom.className = classList.join(' ');

        return this;
    },

    
    removeCls: function(names, prefix, suffix) {
        if (!names) {
            return this;
        }

        if (!this.isSynchronized) {
            this.synchronize();
        }


        if (!suffix) {
            suffix = '';
        }

        var dom = this.dom,
            map = this.hasClassMap,
            classList = this.classList,
            SEPARATOR = this.SEPARATOR,
            i, ln, name;

        prefix = prefix ? prefix + SEPARATOR : '';
        suffix = suffix ? SEPARATOR + suffix : '';

        if (typeof names == 'string') {
            names = names.split(this.spacesRegex);
        }

        for (i = 0,ln = names.length; i < ln; i++) {
            name = prefix + names[i] + suffix;

            if (map[name]) {
                delete map[name];
                Ext.Array.remove(classList, name);
            }
        }

        dom.className = classList.join(' ');

        return this;
    },

    replaceCls: function(oldName, newName, prefix, suffix) {
        return this.removeCls(oldName, prefix, suffix).addCls(newName, prefix, suffix);
    },

    hasCls: function(name) {
        if (!this.isSynchronized) {
            this.synchronize();
        }

        return this.hasClassMap.hasOwnProperty(name);
    },

    show: function() {
        this.dom.style.display = '';
    },

    hide: function() {
        this.dom.style.display = 'none !important';
    },

    setHtml: function(html) {
        this.dom.innerHTML = html;
    },

    setHTML: function() {
        this.setHtml.apply(this, arguments);
    },

    setText: function(text) {
        this.dom.textContent = text;
    },

    setWidth: function(width) {
        return this.setLengthValue(this.WIDTH, width);
    },

    setHeight: function(height) {
        return this.setLengthValue(this.HEIGHT, height);
    },

    setTop: function(top) {
        return this.setLengthValue(this.TOP, top);
    },

    setRight: function(right) {
        return this.setLengthValue(this.RIGHT, right);
    },

    setBottom: function(bottom) {
        return this.setLengthValue(this.BOTTOM, bottom);
    },

    setLeft: function(left) {
        return this.setLengthValue(this.LEFT, left);
    },

    setMargin: function(margin) {
        if (margin || margin === 0) {
            margin = this.self.unitizeBox((margin === true) ? 5 : margin);
        }
        else {
            margin = null;
        }
        this.dom.style.margin = margin;
    },

    setPadding: function(padding) {
        if (padding || padding === 0) {
            padding = this.self.unitizeBox((padding === true) ? 5 : padding);
        }
        else {
            padding = null;
        }
        this.dom.style.padding = padding;
    },

    setBorder: function(border) {
        if (border || border === 0) {
            border = this.self.unitizeBox((border === true) ? 1 : border);
        }
        else {
            border = null;
        }
        this.dom.style.borderWidth = border;
    },

    setLengthValue: function(name, value) {
        if (typeof value == 'number') {
            value = value + 'px';
        }

        this.dom.style[name] = value;

        return this;
    },

    getParent: function() {
        return Ext.get(this.dom.parentNode);
    },

    getFirstChild: function() {
        return Ext.get(this.dom.firstElementChild);
    },

    append: function(element) {
        this.dom.appendChild(Ext.getDom(element));

        return this;
    },

    insertFirst: function(element) {
        var elementDom = Ext.getDom(element),
            dom = this.dom,
            firstChild = dom.firstChild;

        if (!firstChild) {
            dom.appendChild(elementDom);
        }
        else {
            dom.insertBefore(elementDom, firstChild);
        }

        return this;
    },

    wrap: function(config, domNode) {
        var dom = this.dom,
            wrapper = this.self.create(config, domNode),
            wrapperDom = (domNode) ? wrapper : wrapper.dom,
            parentNode = dom.parentNode;

        if (parentNode) {
            parentNode.insertBefore(wrapperDom, dom);
        }

        wrapperDom.appendChild(dom);

        return wrapper;
    },

    wrapAllChildren: function(config) {
        var dom = this.dom,
            children = dom.childNodes,
            wrapper = this.self.create(config),
            wrapperDom = wrapper.dom;

        while (children.length > 0) {
            wrapperDom.appendChild(dom.firstChild);
        }

        dom.appendChild(wrapperDom);

        return wrapper;
    },

    unwrapAllChildren: function() {
        var dom = this.dom,
            children = dom.childNodes,
            parentNode = dom.parentNode;

        if (parentNode) {
            while (children.length > 0) {
                parentNode.insertBefore(dom, dom.firstChild);
            }

            this.destroy();
        }
    },

    unwrap: function() {
        var dom = this.dom,
            parentNode = dom.parentNode,
            grandparentNode;

        if (parentNode) {
            grandparentNode = parentNode.parentNode;
            grandparentNode.insertBefore(dom, parentNode);
            grandparentNode.removeChild(parentNode);
        }
        else {
            grandparentNode = document.createDocumentFragment();
            grandparentNode.appendChild(dom);
        }

        return this;
    },

    redraw: function() {
        var dom = this.dom,
            domStyle = dom.style;

        domStyle.display = 'none';
        dom.offsetHeight;
        domStyle.display = '';
    },

    isPainted: function() {
        return Boolean(this.dom.offsetParent);
    },

    destroy: function() {
        this.destroy = Ext.emptyFn;

        var cache = this.self.cache,
            dom = this.dom;

        if (dom && dom.parentNode && dom.tagName != 'BODY') {
            dom.parentNode.removeChild(dom);
            delete cache[this.id];
        }

        delete this.dom;
    }

}, function(Element) {
    Ext.elements = Ext.cache = Element.cache;

    Ext.get = function(element) {
        return Element.get.call(Element, element);
    }
});


Ext.define('Ext.dom.CompositeElementLite', {
    alternateClassName: ['Ext.CompositeElementLite', 'Ext.CompositeElement'],

    requires: ['Ext.dom.Element'],

    statics: {
        
        importElementMethods: function() {
            var name,
                elementPrototype = Ext.dom.Element.prototype,
                prototype = this.prototype;

            for (name in elementPrototype) {
                if (typeof elementPrototype[name] == 'function'){
                    (function(key) {
                        prototype[key] = prototype[key] || function() {
                            return this.invoke(key, arguments);
                        };
                    }).call(prototype, name);

                }
            }
        }
    },

    constructor: function(elements, root) {
        
        this.elements = [];
        this.add(elements, root);
        this.el = new Ext.dom.AbstractElement.Fly();
    },

    isComposite: true,

    
    getElement: function(el) {
        
        return this.el.attach(el);
    },

    
    transformElement: function(el) {
        return Ext.getDom(el);
    },

    
    getCount: function() {
        return this.elements.length;
    },

    
    add: function(els, root) {
        var elements = this.elements,
            i, ln;

        if (!els) {
            return this;
        }

        if (typeof els == "string") {
            els = Ext.dom.Element.selectorFunction(els, root);
        }
        else if (els.isComposite) {
            els = els.elements;
        }
        else if (!Ext.isIterable(els)) {
            els = [els];
        }

        for (i = 0, ln = els.length; i < ln; ++i) {
            elements.push(this.transformElement(els[i]));
        }

        return this;
    },

    invoke: function(fn, args) {
        var elements = this.elements,
            ln = elements.length,
            element,
            i;

        for (i = 0; i < ln; i++) {
            element = elements[i];

            if (element) {
                Ext.dom.Element.prototype[fn].apply(this.getElement(element), args);
            }
        }
        return this;
    },

    
    item: function(index) {
        var el = this.elements[index],
            out = null;

        if (el) {
            out = this.getElement(el);
        }

        return out;
    },

    
    addListener: function(eventName, handler, scope, opt) {
        var els = this.elements,
                len = els.length,
                i, e;

        for (i = 0; i < len; i++) {
            e = els[i];
            if (e) {
                Ext.EventManager.on(e, eventName, handler, scope || e, opt);
            }
        }
        return this;
    },
    
    each: function(fn, scope) {
        var me = this,
                els = me.elements,
                len = els.length,
                i, e;

        for (i = 0; i < len; i++) {
            e = els[i];
            if (e) {
                e = this.getElement(e);
                if (fn.call(scope || e, e, me, i) === false) {
                    break;
                }
            }
        }
        return me;
    },

    
    fill: function(els) {
        var me = this;
        me.elements = [];
        me.add(els);
        return me;
    },

    
    filter: function(selector) {
        var els = [],
                me = this,
                fn = Ext.isFunction(selector) ? selector
                        : function(el) {
                    return el.is(selector);
                };

        me.each(function(el, self, i) {
            if (fn(el, i) !== false) {
                els[els.length] = me.transformElement(el);
            }
        });

        me.elements = els;
        return me;
    },

    
    indexOf: function(el) {
        return Ext.Array.indexOf(this.elements, this.transformElement(el));
    },

    
    replaceElement: function(el, replacement, domReplace) {
        var index = !isNaN(el) ? el : this.indexOf(el),
                d;
        if (index > -1) {
            replacement = Ext.getDom(replacement);
            if (domReplace) {
                d = this.elements[index];
                d.parentNode.insertBefore(replacement, d);
                Ext.removeNode(d);
            }
            Ext.Array.splice(this.elements, index, 1, replacement);
        }
        return this;
    },

    
    clear: function() {
        this.elements = [];
    },

    addElements: function(els, root) {
        if (!els) {
            return this;
        }

        if (typeof els == "string") {
            els = Ext.dom.Element.selectorFunction(els, root);
        }

        var yels = this.elements;

        Ext.each(els, function(e) {
            yels.push(Ext.get(e));
        });

        return this;
    },

    
    first: function() {
        return this.item(0);
    },

    
    last: function() {
        return this.item(this.getCount() - 1);
    },

    
    contains: function(el) {
        return this.indexOf(el) != -1;
    },

    
    removeElement: function(keys, removeDom) {
        var me = this,
                elements = this.elements,
                el;

        Ext.each(keys, function(val) {
            if ((el = (elements[val] || elements[val = me.indexOf(val)]))) {
                if (removeDom) {
                    if (el.dom) {
                        el.remove();
                    }
                    else {
                        Ext.removeNode(el);
                    }
                }
                Ext.Array.erase(elements, val, 1);
            }
        });

        return this;
    }

}, function() {
    this.importElementMethods();

    this.prototype.on = this.prototype.addListener;

    if (Ext.DomQuery){
        Ext.dom.Element.selectorFunction = Ext.DomQuery.select;
    }

    
   Ext.dom.Element.select = function(selector, root) {
        var elements;

        if (typeof selector == "string") {
            elements = Ext.dom.Element.selectorFunction(selector, root);
        }
        else if (selector.length !== undefined) {
            elements = selector;
        }
        else {
            throw new Error("[Ext.select] Invalid selector specified: " + selector);
        }

        return new Ext.CompositeElementLite(elements);
    };

    
    Ext.select = function() {
        return Ext.dom.Element.select.apply(Ext.dom.Element, arguments);
    };
});

Ext.define('Ext.event.publisher.Dom', {
    extend: 'Ext.event.publisher.Publisher',

    requires: [
        'Ext.env.Browser',
        'Ext.Element',
        'Ext.event.Dom'
    ],

    targetType: 'element',

    idOrClassSelectorRegex: /^([#|\.])([\w\-]+)$/,

    handledEvents: ['click', 'focus', 'blur',
                    'mousemove', 'mousedown', 'mouseup', 'mouseover', 'mouseout',
                    'keyup', 'keydown', 'keypress',
                    'transitionend', 'animationstart', 'animationend'],

    classNameSplitRegex: /\s+/,

    SELECTOR_ALL: '*',

    constructor: function() {
        var eventNames = this.getHandledEvents(),
            eventNameMap = {},
            i, ln, eventName, vendorEventName;

        this.doBubbleEventsMap = {
            'click': true,
            'mousedown': true,
            'mousemove': true,
            'mouseup': true,
            'mouseover': true,
            'mouseout': true,
            'transitionend': true
        };

        this.onEvent = Ext.Function.bind(this.onEvent, this);

        this.subscribers = {};

        for (i = 0,ln = eventNames.length; i < ln; i++) {
            eventName = eventNames[i];
            vendorEventName = this.getVendorEventName(eventName);
            eventNameMap[vendorEventName] = eventName;

            this.attachListener(vendorEventName);
        }

        this.eventNameMap = eventNameMap;

        return this.callParent();
    },

    getSubscribers: function(eventName) {
        var subscribers = this.subscribers,
            eventSubscribers = subscribers[eventName];

        if (!eventSubscribers) {
            eventSubscribers = subscribers[eventName] = {
                id: {
                    $length: 0
                },
                className: {
                    $length: 0
                },
                selector: [],
                $length: 0
            }
        }

        return eventSubscribers;
    },

    getVendorEventName: function(eventName) {
        if (eventName === 'transitionend') {
            eventName = Ext.browser.getVendorProperyName('transitionEnd');
        }
        else if (eventName === 'animationstart') {
            eventName = Ext.browser.getVendorProperyName('animationStart');
        }
        else if (eventName === 'animationend') {
            eventName = Ext.browser.getVendorProperyName('animationEnd');
        }

        return eventName;
    },

    attachListener: function(eventName) {
        document.addEventListener(eventName, this.onEvent, !this.doesEventBubble(eventName));

        return this;
    },

    removeListener: function(eventName) {
        document.removeEventListener(eventName, this.onEvent, !this.doesEventBubble(eventName));

        return this;
    },

    doesEventBubble: function(eventName) {
        return !!this.doBubbleEventsMap[eventName];
    },

    subscribe: function(target, eventName) {
        if (!this.handles(eventName)) {
            return false;
        }

        var idOrClassSelectorMatch = target.match(this.idOrClassSelectorRegex),
            subscribers = this.getSubscribers(eventName),
            idSubscribers = subscribers.id,
            classNameSubscribers = subscribers.className,
            selectorSubscribers = subscribers.selector,
            type, value;

        if (idOrClassSelectorMatch !== null) {
            type = idOrClassSelectorMatch[1];
            value = idOrClassSelectorMatch[2];

            if (type === '#') {
                if (idSubscribers[value]) {
                    return true;
                }

                idSubscribers[value] = true;
                idSubscribers.$length++;
            }
            else {
                if (classNameSubscribers[value]) {
                    return true;
                }

                classNameSubscribers[value] = true;
                classNameSubscribers.$length++;
            }
        }
        else {
            if (selectorSubscribers[target]) {
                return true;
            }

            selectorSubscribers[target] = true;
            selectorSubscribers.push(target);
        }

        subscribers.$length++;

        return true;
    },

    unsubscribe: function(target, eventName) {
        if (!this.handles(eventName)) {
            return false;
        }

        var idOrClassSelectorMatch = target.match(this.idOrClassSelectorRegex),
            subscribers = this.getSubscribers(eventName),
            idSubscribers = subscribers.id,
            classNameSubscribers = subscribers.className,
            selectorSubscribers = subscribers.selector,
            type, value;

        if (idOrClassSelectorMatch !== null) {
            type = idOrClassSelectorMatch[1];
            value = idOrClassSelectorMatch[2];

            if (type === '#') {
                if (!idSubscribers[value]) {
                    return true;
                }

                delete idSubscribers[value];
                idSubscribers.$length--;
            }
            else {
                if (!classNameSubscribers[value]) {
                    return true;
                }

                delete classNameSubscribers[value];
                classNameSubscribers.$length--;
            }
        }
        else {
            if (!selectorSubscribers[target]) {
                return true;
            }

            delete selectorSubscribers[target];
            Ext.Array.remove(selectorSubscribers, target);
        }

        subscribers.$length--;

        return true;
    },

    getElementTarget: function(target) {
        if (target.nodeType !== 1) {
            target = target.parentNode;

            if (!target || target.nodeType !== 1) {
                return null;
            }
        }

        return target;
    },

    getBubblingTargets: function(target) {
        var targets = [];

        if (!target) {
            return targets;
        }

        do {
            targets[targets.length] = target;

            target = target.parentNode;
        } while (target && target.nodeType === 1);

        return targets;
    },

    dispatch: function(target, eventName, args) {
        
        args.push(args[0].target);

        this.callParent(arguments);
    },

    publish: function(eventName, targets, event) {
        var subscribers = this.getSubscribers(eventName),
            wildcardSubscribers;

        if (subscribers.$length === 0 || !this.doPublish(subscribers, eventName, targets, event)) {
            wildcardSubscribers = this.getSubscribers('*');

            if (wildcardSubscribers.$length > 0) {
                this.doPublish(wildcardSubscribers, eventName, targets, event);
            }
        }

        return this;
    },

    doPublish: function(subscribers, eventName, targets, event) {
        var idSubscribers = subscribers.id,
            classNameSubscribers = subscribers.className,
            selectorSubscribers = subscribers.selector,
            hasIdSubscribers = idSubscribers.$length > 0,
            hasClassNameSubscribers = classNameSubscribers.$length > 0,
            hasSelectorSubscribers = selectorSubscribers.length > 0,
            isClassNameHandled = {},
            args = [event],
            hasDispatched = false,
            classNameSplitRegex = this.classNameSplitRegex,
            allSelector = this.SELECTOR_ALL,
            i, ln, j, subLn, target, id, className, classNames, selector;

        for (i = 0,ln = targets.length; i < ln; i++) {
            target = targets[i];
            event.setDelegatedTarget(target);

            if (hasIdSubscribers) {
                id = target.id;

                if (id) {
                    if (idSubscribers[id] === true) {
                        hasDispatched = true;
                        this.dispatch('#' + id, eventName, args);
                    }
                }
            }

            if (hasClassNameSubscribers) {
                className = target.className;

                if (className) {
                    classNames = className.split(classNameSplitRegex);

                    for (j = 0,subLn = classNames.length; j < subLn; j++) {
                        className = classNames[j];

                        if (!isClassNameHandled[className]) {
                            isClassNameHandled[className] = true;

                            if (classNameSubscribers[className] === true) {
                                hasDispatched = true;
                                this.dispatch('.' + className, eventName, args);
                            }
                        }
                    }
                }
            }

            
            if (event.isStopped) {
                return hasDispatched;
            }
        }

        if (hasSelectorSubscribers) {
            for (i = 0,ln = selectorSubscribers.length; i < ln; i++) {
                selector = selectorSubscribers[i];

                if (selector === allSelector && !hasDispatched) {
                    event.setDelegatedTarget(event.browserEvent.target);
                    hasDispatched = true;
                    this.dispatch(allSelector, eventName, args);
                }
                else {
                    for (j = 0,subLn = targets.length; j < subLn; j++) {
                        target = targets[j];

                        if (this.matchesSelector(target, selector)) {
                            event.setDelegatedTarget(target);
                            hasDispatched = true;
                            this.dispatch(selector, eventName, args);
                        }

                        if (event.isStopped) {
                            return hasDispatched;
                        }
                    }
                }

                if (event.isStopped) {
                    return hasDispatched;
                }
            }
        }

        return hasDispatched;
    },

    matchesSelector: function(element, selector) {
        if ('webkitMatchesSelector' in element) {
            return element.webkitMatchesSelector(selector);
        }

        return Ext.DomQuery.is(element, selector);
    },

    onEvent: function(e) {
        var eventName = this.eventNameMap[e.type];

        if (!eventName || this.getSubscribersCount(eventName) === 0) {
            return;
        }

        var target = this.getElementTarget(e.target),
            targets;

        if (!target) {
            return;
        }

        if (this.doesEventBubble(eventName)) {
            targets = this.getBubblingTargets(target);
        }
        else {
            targets = [target];
        }

        this.publish(eventName, targets, new Ext.event.Dom(e));
    },

    hasSubscriber: function(target, eventName) {
        if (!this.handles(eventName)) {
            return false;
        }

        var match = target.match(this.idOrClassSelectorRegex),
            subscribers = this.getSubscribers(eventName),
            type, value;

        if (match !== null) {
            type = match[1];
            value = match[2];

            if (type === '#') {
                return !!subscribers.id[value];
            }
            else {
                return !!subscribers.className[value];
            }
        }
        else {
            return (!!subscribers.selector[target] && Ext.Array.indexOf(subscribers.selector, target) !== -1);
        }

        return false;
    },

    getSubscribersCount: function(eventName) {
        if (!this.handles(eventName)) {
            return 0;
        }

        return this.getSubscribers(eventName).$length + this.getSubscribers('*').$length;
    }

});

Ext.define('Ext.event.publisher.TouchGesture', {

    extend: 'Ext.event.publisher.Dom',

    requires: [
        'Ext.util.Point',
        'Ext.event.Touch'
    ],

    handledEvents: ['touchstart', 'touchmove', 'touchend', 'touchcancel'],

    moveEventName: 'touchmove',

    config: {
        moveThrottle: 3,
        buffering: {
            enabled: false,
            interval: 10
        },
        recognizers: {}
    },

    currentTouchesCount: 0,

    constructor: function(config) {
        this.processEvents = Ext.Function.bind(this.processEvents, this);

        this.eventProcessors = {
            touchstart: this.onTouchStart,
            touchmove: this.onTouchMove,
            touchend: this.onTouchEnd,
            touchcancel: this.onTouchEnd
        };

        this.eventToRecognizerMap = {};

        this.activeRecognizers = [];

        this.currentRecognizers = [];

        this.currentTargets = {};

        this.currentTouches = {};

        this.buffer = [];

        this.initConfig(config);

        return this.callParent();
    },

    applyBuffering: function(buffering) {
        if (buffering.enabled === true) {
            this.bufferTimer = setInterval(this.processEvents, buffering.interval);
        }
        else {
            clearInterval(this.bufferTimer);
        }

        return buffering;
    },

    applyRecognizers: function(recognizers) {
        var i, recognizer;

        for (i in recognizers) {
            if (recognizers.hasOwnProperty(i)) {
                recognizer = recognizers[i];

                this.registerRecognizer(recognizer);
            }
        }

        return recognizers;
    },

    handles: function(eventName) {
        return this.callParent(arguments) || this.eventToRecognizerMap.hasOwnProperty(eventName);
    },

    doesEventBubble: function() {
        
        return true;
    },

    onEvent: function(e) {
        var buffering = this.getBuffering();

        e = new Ext.event.Touch(e);

        if (buffering.enabled) {
            this.buffer.push(e);
        }
        else {
            this.processEvent(e);
        }
    },

    processEvents: function() {
        var buffer = this.buffer,
            ln = buffer.length,
            moveEvents = [],
            events, event, i;

        if (ln > 0) {
            events = buffer.slice(0);
            buffer.length = 0;

            for (i = 0; i < ln; i++) {
                event = events[i];
                if (event.type === this.moveEventName) {
                    moveEvents.push(event);
                }
                else {
                    if (moveEvents.length > 0) {
                        this.processEvent(this.mergeEvents(moveEvents));
                        moveEvents.length = 0;
                    }

                    this.processEvent(event);
                }
            }

            if (moveEvents.length > 0) {
                this.processEvent(this.mergeEvents(moveEvents));
                moveEvents.length = 0;
            }
        }
    },

    mergeEvents: function(events) {
        var changedTouchesLists = [],
            ln = events.length,
            i, event, targetEvent;

        targetEvent = events[ln - 1];

        if (ln === 1) {
            return targetEvent;
        }

        for (i = 0; i < ln; i++) {
            event = events[i];
            changedTouchesLists.push(event.changedTouches);
        }

        targetEvent.changedTouches = this.mergeTouchLists(changedTouchesLists);

        return targetEvent;
    },

    mergeTouchLists: function(touchLists) {
        var touches = {},
            list = [],
            i, ln, touchList, j, subLn, touch, identifier;

        for (i = 0,ln = touchLists.length; i < ln; i++) {
            touchList = touchLists[i];

            for (j = 0,subLn = touchList.length; j < subLn; j++) {
                touch = touchList[j];
                identifier = touch.identifier;
                touches[identifier] = touch;
            }
        }

        for (identifier in touches) {
            if (touches.hasOwnProperty(identifier)) {
                list.push(touches[identifier]);
            }
        }

        return list;
    },
    
    registerRecognizer: function(recognizer) {
        var map = this.eventToRecognizerMap,
            activeRecognizers = this.activeRecognizers,
            handledEvents = recognizer.getHandledEvents(),
            i, ln, eventName;

        recognizer.setOnRecognized(this.onRecognized);
        recognizer.setCallbackScope(this);

        for (i = 0,ln = handledEvents.length; i < ln; i++) {
            eventName = handledEvents[i];

            map[eventName] = recognizer;
        }

        activeRecognizers.push(recognizer);

        return this;
    },

    onRecognized: function(eventName, e, touches, info) {
        var targetGroups = [],
            ln = touches.length,
            targets, i, touch;

        if (ln === 1) {
            return this.publish(eventName, touches[0].targets, e, info);
        }

        for (i = 0; i < ln; i++) {
            touch = touches[i];
            targetGroups.push(touch.targets);
        }

        targets = this.getCommonTargets(targetGroups);

        this.publish(eventName, targets, e, info);
    },

    publish: function(eventName, targets, event, info) {
        event.set(info);

        return this.callParent([eventName, targets, event]);
    },

    getCommonTargets: function(targetGroups) {
        var firstTargetGroup = targetGroups[0],
            ln = targetGroups.length;

        if (ln === 1) {
            return firstTargetGroup;
        }

        var commonTargets = [],
            i = 1,
            target, targets, j;

        while (true) {
            target = firstTargetGroup[firstTargetGroup.length - i];

            if (!target) {
                return commonTargets;
            }

            for (j = 1; j < ln; j++) {
                targets = targetGroups[j];

                if (targets[targets.length - i] !== target) {
                    return commonTargets;
                }
            }

            commonTargets.unshift(target);
            i++;
        }

        return commonTargets;
    },

    invokeRecognizers: function(methodName, e) {
        var recognizers = this.activeRecognizers,
            ln = recognizers.length,
            i, recognizer;

        if (methodName === 'onStart') {
            for (i = 0; i < ln; i++) {
                recognizers[i].isActive = true;
            }
        }

        for (i = 0; i < ln; i++) {
            recognizer = recognizers[i];
            if (recognizer.isActive && recognizer[methodName].call(recognizer, e) === false) {
                recognizer.isActive = false;
            }
        }
    },

    getActiveRecognizers: function() {
        return this.activeRecognizers;
    },

    processEvent: function(e) {
        this.eventProcessors[e.type].call(this, e);
    },

    onTouchStart: function(e) {
        var currentTargets = this.currentTargets,
            currentTouches = this.currentTouches,
            currentTouchesCount = this.currentTouchesCount,
            changedTouches = e.changedTouches,
            ln = changedTouches.length,
            i, touch, identifier;

        
        
        currentTouchesCount += ln;
        if (currentTouchesCount > e.touches.length) {
            return;
        }

        for (i = 0; i < ln; i++) {
            this.currentTouchesCount++;
            touch = changedTouches[i];
            identifier = touch.identifier;
            currentTouches[identifier] = touch;
            currentTargets[identifier] = this.getBubblingTargets(this.getElementTarget(touch.target));
        }

        e.setTargets(currentTargets);

        for (i = 0; i < ln; i++) {
            touch = changedTouches[i];

            this.publish('touchstart', touch.targets, e, {touch: touch});
        }

        if (!this.isStarted) {
            this.isStarted = true;
            this.invokeRecognizers('onStart', e);
        }

        this.invokeRecognizers('onTouchStart', e);
    },

    onTouchMove: function(e) {
        if (!this.isStarted) {
            return;
        }

        var currentTargets = this.currentTargets,
            currentTouches = this.currentTouches,
            moveThrottle = this.getMoveThrottle(),
            changedTouches = e.changedTouches,
            stillTouchesCount = 0,
            i, ln, touch, point, oldPoint, identifier;

        e.setTargets(currentTargets);

        for (i = 0,ln = changedTouches.length; i < ln; i++) {
            touch = changedTouches[i];
            identifier = touch.identifier;
            point = touch.point;

            oldPoint = currentTouches[identifier].point;

            if (moveThrottle && point.isCloseTo(oldPoint, moveThrottle)) {
                stillTouchesCount++;
                continue;
            }

            currentTouches[identifier] = touch;

            this.publish('touchmove', touch.targets, e, {touch: touch});
        }

        if (stillTouchesCount < ln) {
            this.invokeRecognizers('onTouchMove', e);
        }
    },

    onTouchEnd: function(e) {
        if (!this.isStarted) {
            return;
        }

        var currentTargets = this.currentTargets,
            currentTouches = this.currentTouches,
            changedTouches = e.changedTouches,
            ln = changedTouches.length,
            isEnded = false,
            identifier, i, touch;

        e.setTargets(currentTargets);

        this.currentTouchesCount -= ln;

        isEnded = (this.currentTouchesCount === 0);

        if (isEnded) {
            this.isStarted = false;
        }

        for (i = 0; i < ln; i++) {
            touch = changedTouches[i];
            identifier = touch.identifier;

            delete currentTouches[identifier];
            delete currentTargets[identifier];

            this.publish('touchend', touch.targets, e, {touch: touch});
        }

        this.invokeRecognizers('onTouchEnd', e);

        if (isEnded) {
            this.invokeRecognizers('onEnd', e);
        }
    }

}, function() {
    if (!Ext.feature.has.Touch) {
        this.override({
            moveEventName: 'mousemove',

            map: {
                mouseToTouch: {
                    mousedown: 'touchstart',
                    mousemove: 'touchmove',
                    mouseup: 'touchend'
                },

                touchToMouse: {
                    touchstart: 'mousedown',
                    touchmove: 'mousemove',
                    touchend: 'mouseup'
                }
            },

            attachListener: function(eventName) {
                eventName = this.map.touchToMouse[eventName];

                if (!eventName) {
                    return;
                }

                return this.callOverridden([eventName]);
            },

            lastEventType: null,

            onEvent: function(e) {
                var type = e.type,
                    touchList = [e];

                
                
                
                if (type === 'mousedown' && this.lastEventType && this.lastEventType !== 'mouseup') {
                    var fixedEvent = document.createEvent("MouseEvent");
                        fixedEvent.initMouseEvent('mouseup', e.bubbles, e.cancelable,
                            document.defaultView, e.detail, e.screenX, e.screenY, e.clientX,
                            e.clientY, e.ctrlKey, e.altKey, e.shiftKey, e.metaKey, e.metaKey,
                            e.button, e.relatedTarget);

                    this.onEvent(fixedEvent);
                }

                if (type !== 'mousemove') {
                    this.lastEventType = type;
                }

                e.identifier = 1;
                e.touches = (type !== 'mouseup') ? touchList : [];
                e.targetTouches = (type !== 'mouseup') ? touchList : [];
                e.changedTouches = touchList;

                return this.callOverridden([e]);
            },

            processEvent: function(e) {
                this.eventProcessors[this.map.mouseToTouch[e.type]].call(this, e);
            }
        });
    }
});


Ext.define('Ext.fx.runner.Css', {
    extend: 'Ext.EventedBase',

    requires: [
        'Ext.fx.Animation'
    ],

    prefixedProperties: {
        'transform'                 : true,
        'transform-origin'          : true,
        'perspective'               : true,
        'transform-style'           : true,
        'transition'                : true,
        'transition-property'       : true,
        'transition-duration'       : true,
        'transition-timing-function': true,
        'transition-delay'          : true,
        'animation'                 : true,
        'animation-name'            : true,
        'animation-duration'        : true,
        'animation-iteration-count' : true,
        'animation-direction'       : true,
        'animation-timing-function' : true,
        'animation-delay'           : true
    },

    lengthProperties: {
        'top'                : true,
        'right'              : true,
        'bottom'             : true,
        'left'               : true,
        'width'              : true,
        'height'             : true,
        'max-height'         : true,
        'max-width'          : true,
        'min-height'         : true,
        'min-width'          : true,
        'margin-bottom'      : true,
        'margin-left'        : true,
        'margin-right'       : true,
        'margin-top'         : true,
        'padding-bottom'     : true,
        'padding-left'       : true,
        'padding-right'      : true,
        'padding-top'        : true,
        'border-bottom-width': true,
        'border-left-width'  : true,
        'border-right-width' : true,
        'border-spacing'     : true,
        'border-top-width'   : true,
        'border-width'       : true,
        'outline-width'      : true,
        'letter-spacing'     : true,
        'line-height'        : true,
        'text-indent'        : true,
        'word-spacing'       : true,
        'font-size'          : true,
        'translate'          : true,
        'translateX'         : true,
        'translateY'         : true,
        'translateZ'         : true,
        'translate3d'        : true
    },

    durationProperties: {
        'transition-duration'   : true,
        'transition-delay'      : true,
        'animation-duration'    : true,
        'animation-delay'       : true
    },

    angleProperties: {
        rotate     : true,
        rotateX    : true,
        rotateY    : true,
        rotateZ    : true,
        skew       : true,
        skewX      : true,
        skewY      : true
    },

    lengthUnitRegex: /([a-z%]*)$/,

    DEFAULT_UNIT_LENGTH: 'px',

    DEFAULT_UNIT_ANGLE: 'deg',

    DEFAULT_UNIT_DURATION: 'ms',

    formattedNameCache: {},

    constructor: function() {
        var supports3dTransform = Ext.feature.has.Css3dTransforms;

        if (supports3dTransform) {
            this.transformMethods = ['translateX', 'translateY', 'translateZ', 'rotate', 'rotateX', 'rotateY', 'rotateZ', 'skewX', 'skewY', 'scaleX', 'scaleY', 'scaleZ'];
        }
        else {
            this.transformMethods = ['translateX', 'translateY', 'rotate', 'skewX', 'skewY', 'scaleX', 'scaleY'];
        }

        this.vendorPrefix = Ext.browser.getStyleDashPrefix();

        this.supports3dTransforms = supports3dTransform;

        this.ruleStylesCache = {};

        return this;

    },

    getStyleSheet: function() {
        var styleSheet = this.styleSheet,
            styleElement, styleSheets;

        if (!styleSheet) {
            styleElement = document.createElement('style');
            styleElement.type = 'text/css';

            (document.head || document.getElementsByTagName('head')[0]).appendChild(styleElement);

            styleSheets = document.styleSheets;

            this.styleSheet = styleSheet = styleSheets[styleSheets.length - 1];
        }

        return styleSheet;
    },

    applyRules: function(selectors) {
        var styleSheet = this.getStyleSheet(),
            ruleStylesCache = this.ruleStylesCache,
            rules = styleSheet.cssRules,
            selector, properties, ruleStyle,
            ruleStyleCache, rulesLength, name, value;

        for (selector in selectors) {
            properties = selectors[selector];

            ruleStyle = ruleStylesCache[selector];

            if (ruleStyle === undefined) {
                rulesLength = rules.length;
                styleSheet.insertRule(selector + '{}', rulesLength);
                ruleStyle = ruleStylesCache[selector] = rules.item(rulesLength).style;
            }

            ruleStyleCache = ruleStyle.$cache;

            if (!ruleStyleCache) {
                ruleStyleCache = ruleStyle.$cache = {};
            }

            for (name in properties) {
                value = this.formatValue(properties[name], name);
                name = this.formatName(name);

                if (ruleStyleCache[name] !== value) {
                    ruleStyleCache[name] = value;

                    ruleStyle.setProperty(name, value, 'important');
                }
            }
        }

        return this;
    },

    applyStyles: function(styles) {
        var id, element, elementStyle, properties, name, value;

        for (id in styles) {
            element = document.getElementById(id);
            elementStyle = element.style;

            properties = styles[id];

            for (name in properties) {
                value = this.formatValue(properties[name], name);
                name = this.formatName(name);


                elementStyle.setProperty(name, value, 'important');
            }
        }

        return this;
    },

    formatName: function(name) {
        var cache = this.formattedNameCache,
            formattedName = cache[name];

        if (!formattedName) {
            if (this.prefixedProperties[name]) {
                formattedName = this.vendorPrefix + name;
            }
            else {
                formattedName = name;
            }

            cache[name] = formattedName;
        }

        return formattedName;
    },

    formatValue: function(value, name) {
        var type = typeof value,
            lengthUnit = this.DEFAULT_UNIT_LENGTH,
            transformMethods,
            method, i, ln,
            transformValues, values, unit;

        if (type == 'string') {
            if (this.lengthProperties[name]) {
                unit = value.match(this.lengthUnitRegex)[1];

                if (unit.length > 0) {
                    if (unit !== lengthUnit) {
                        Ext.Logger.error("Length unit: '" + unit + "' in value: '" + value + "' of property: '" + name + "' is not " +
                                "valid for animation. Only 'px' is allowed");
                    }
                }
                else {
                    return value + lengthUnit;
                }
            }

            return value;
        }
        else if (type == 'number') {
            if (value == 0) {
                return '0';
            }

            if (this.lengthProperties[name]) {
                return value + lengthUnit;
            }

            if (this.angleProperties[name]) {
                return value + this.DEFAULT_UNIT_ANGLE;
            }

            if (this.durationProperties[name]) {
                return value + this.DEFAULT_UNIT_DURATION;
            }
        }
        else if (name === 'transform') {
            transformMethods = this.transformMethods;
            transformValues = [];

            for (i = 0,ln = transformMethods.length; i < ln; i++) {
                method = transformMethods[i];

                transformValues.push(method + '(' + this.formatValue(value[method], method) + ')');
            }

            return transformValues.join(' ');
        }
        else if (Ext.isArray(value)) {
            values = [];

            for (i = 0,ln = value.length; i < ln; i++) {
                values.push(this.formatValue(value[i], name));
            }

            return (values.length > 0) ? values.join(', ') : 'none';
        }

        return value;
    }
});


Ext.define('Ext.fx.runner.CssAnimation', {
    extend: 'Ext.fx.runner.Css',

    constructor: function() {
        this.runningAnimationsMap = {};

        this.elementEndStates = {};

        this.animationElementMap = {};

        this.keyframesRulesCache = {};

        this.uniqueId = 0;

        return this.callParent(arguments);
    },

    attachListeners: function() {
        var eventDispatcher = this.getEventDispatcher();

        this.listenersAttached = true;

        eventDispatcher.addListener('element', '*', 'animationstart', 'onAnimationStart', this);
        eventDispatcher.addListener('element', '*', 'animationend', 'onAnimationEnd', this);
    },

    onAnimationStart: function(e) {
        var name = e.browserEvent.animationName,
            elementId = this.animationElementMap[name],
            animation = this.runningAnimationsMap[elementId][name],
            elementEndStates = this.elementEndStates,
            elementEndState = elementEndStates[elementId],
            data = {};

        console.log("START============= " + name);
        if (elementEndState) {
            delete elementEndStates[elementId];

            data[elementId] = elementEndState;

            this.applyStyles(data);
        }

        if (animation.before) {
            data[elementId] = animation.before;

            this.applyStyles(data);
        }
    },

    onAnimationEnd: function(e) {
        var element = e.target,
            name = e.browserEvent.animationName,
            animationElementMap = this.animationElementMap,
            elementId = animationElementMap[name],
            runningAnimationsMap = this.runningAnimationsMap,
            runningAnimations = runningAnimationsMap[elementId],
            animation = runningAnimations[name];

        console.log("END============= " + name);

        if (animation.onBeforeEnd) {
            animation.onBeforeEnd.call(animation.scope || this, element);
        }

        if (animation.onEnd) {
            animation.onEnd.call(animation.scope || this, element);
        }

        delete animationElementMap[name];
        delete runningAnimations[name];

        this.removeKeyframesRule(name);
    },

    generateAnimationId: function() {
        return 'animation-' + (++this.uniqueId);
    },

    run: function(animations) {
        var data = {},
            elementEndStates = this.elementEndStates,
            animationElementMap = this.animationElementMap,
            runningAnimationsMap = this.runningAnimationsMap,
            runningAnimations, states,
            elementId, animationId, i, ln, animation,
            name, runningAnimation,
            names, durations, easings, delays, directions, iterations;

        if (!this.listenersAttached) {
            this.attachListeners();
        }

        animations = Ext.Array.from(animations);

        for (i = 0,ln = animations.length; i < ln; i++) {
            animation = animations[i];

            animation = Ext.factory(animation, Ext.fx.Animation);
            elementId = animation.getElement().getId();
            animationId = animation.getName() || this.generateAnimationId();

            animationElementMap[animationId] = elementId;

            animation = animation.getData();
            states = animation.states;

            this.addKeyframesRule(animationId, states);

            runningAnimations = runningAnimationsMap[elementId];

            if (!runningAnimations) {
                runningAnimations = runningAnimationsMap[elementId] = {};
            }

            runningAnimations[animationId] = animation;

            names = [];
            durations = [];
            easings = [];
            delays = [];
            directions = [];
            iterations = [];

            for (name in runningAnimations) {
                if (runningAnimations.hasOwnProperty(name)) {
                    runningAnimation = runningAnimations[name];

                    names.push(name);
                    durations.push(runningAnimation.duration);
                    easings.push(runningAnimation.easing);
                    delays.push(runningAnimation.delay);
                    directions.push(runningAnimation.direction);
                    iterations.push(runningAnimation.iteration);
                }
            }

            data[elementId] = {
                'animation-name'            : names,
                'animation-duration'        : durations,
                'animation-timing-function' : easings,
                'animation-delay'           : delays,
                'animation-direction'       : directions,
                'animation-iteration-count' : iterations
            };



            if (animation.preserveEndState) {
                elementEndStates[elementId] = states['100%'];
            }
        }

        this.applyStyles(data);
    },

    addKeyframesRule: function(name, keyframes) {
        var percentage, properties,
            keyframesRule,
            styleSheet, rules, styles, rulesLength, key, value;

        styleSheet = this.getStyleSheet();
        rules = styleSheet.cssRules;
        rulesLength = rules.length;
        styleSheet.insertRule('@' + this.vendorPrefix + 'keyframes ' + name + '{}', rulesLength);

        keyframesRule = rules[rulesLength];

        for (percentage in keyframes) {
            properties = keyframes[percentage];

            rules = keyframesRule.cssRules;
            rulesLength = rules.length;

            styles = [];

            for (key in properties) {
                value = this.formatValue(properties[key], key);
                key = this.formatName(key);

                styles.push(key + ':' + value);
            }

            keyframesRule.insertRule(percentage + '{' + styles.join(';') + '}', rulesLength);
        }

        return this;
    },

    removeKeyframesRule: function(name) {
        var styleSheet = this.getStyleSheet(),
            rules = styleSheet.cssRules,
            i, ln, rule;

        for (i = 0,ln = rules.length; i < ln; i++) {
            rule = rules[i];

            if (rule.name === name) {
                styleSheet.removeRule(i);
                break;
            }
        }

        return this;
    }
});


Ext.define('Ext.fx.runner.CssTransition', {
    extend: 'Ext.fx.runner.Css',

    listenersAttached: false,

    constructor: function() {
        this.requestAnimationFrame = Ext.feature.getSupportedPropertyName(window, 'requestAnimationFrame');

        this.runningData = {};

        this.runningAnimationsData = {};

        return this.callParent(arguments);
    },

    attachListeners: function() {
        this.listenersAttached = true;
        this.getEventDispatcher().addListener('element', '*', 'transitionend', 'onTransitionEnd', this);
    },

    onTransitionEnd: function(e) {
        this.refreshRunningAnimationsData(Ext.get(e.target), [e.browserEvent.propertyName]);
    },

    onAnimationEnd: function(element, animation, isInterrupted) {
        var id = element.getId(),
            endRules = {},
            endData = {
                'transition-property': null,
                'transition-duration': null,
                'transition-timing-function': null,
                'transition-delay': null
            },
            toPropertyNames, i, ln, name;

        endRules[id] = endData;

        if (animation.onBeforeEnd) {
            animation.onBeforeEnd.call(animation.scope || this, element, isInterrupted);
        }

        if (!isInterrupted && !animation.preserveEndState) {
            toPropertyNames = animation.toPropertyNames;

            for (i = 0,ln = toPropertyNames.length; i < ln; i++) {
                name = toPropertyNames[i];
                endData[name] = null;
            }
        }

        if (animation.after) {
            Ext.merge(endData, animation.after);
        }

        this.applyStyles(endRules);

        if (animation.onEnd) {
            animation.onEnd.call(animation.scope || this, element, isInterrupted);
        }
    },

    refreshRunningAnimationsData: function(element, propertyNames, interrupt) {
        var id = element.getId(),
            runningAnimationsData = this.runningAnimationsData,
            animations = runningAnimationsData[id],
            ln, j, subLn, name,
            i, animation, properties;

        if (!animations) {
            return this;
        }

        ln = animations.length;

        if (ln === 0) {
            return this;
        }

        for (i = 0; i < ln; i++) {
            animation = animations[i];
            properties = animation.properties;

            for (j = 0,subLn = propertyNames.length; j < subLn; j++) {
                name = propertyNames[j];

                if (properties[name]) {
                    delete properties[name];
                    animation.length--;
                }
            }

            if (animation.length == 0) {
                animations.splice(i, 1);
                i--;
                ln--;

                this.onAnimationEnd(element, animation.data, interrupt);
            }
        }
    },

   getTestElement: function() {
       var testElement = this.testElement,
           iframe, iframeDocument, iframeStyle;

       if (!testElement) {
           iframe = document.createElement('iframe');
           iframeStyle = iframe.style;
           iframeStyle.visibility = 'hidden !important';
           iframeStyle.width = '0px !important';
           iframeStyle.height = '0px !important';
           iframeStyle.position = 'absolute !important';
           iframeStyle.zIndex = '-1000 !important';

           document.body.appendChild(iframe);
           iframeDocument = iframe.contentDocument;

           this.testElement = testElement = iframeDocument.createElement('div');
           testElement.style.position = 'absolute !important';
           iframeDocument.body.appendChild(testElement);
           this.testElementComputedStyle = window.getComputedStyle(testElement);
       }

       return testElement;
    },

    getCssStyleValue: function(name, value) {
        var testElement = this.getTestElement(),
            computedStyle = this.testElementComputedStyle,
            style = testElement.style;

        style.setProperty(name, value);
        value = computedStyle.getPropertyValue(name);
        style.removeProperty(name);

        return value;
    },

    run: function(animations) {
        var me = this,
            isLengthPropertyMap = this.lengthProperties,
            requestAnimationFrame = this.requestAnimationFrame,
            runningData = this.runningData,
            fromData = {},
            toData = {},
            data = {},
            runningAnimationsData = this.runningAnimationsData,
            previous, element, elementId, from, to, before,
            fromPropertyNames, toPropertyNames, propertyNames,
            doApplyTo, message,
            runningAnimations,
            i, j, ln, animation, propertiesLength, propertiesMap,
            computedStyle, formattedName, name, toFormattedValue,
            computedValue, fromFormattedValue, isLengthProperty;

        if (!this.listenersAttached) {
            this.attachListeners();
        }

        animations = Ext.Array.from(animations);

        for (i = 0,ln = animations.length; i < ln; i++) {
            animation = animations[i];
            animation = Ext.factory(animation, Ext.fx.Animation);
            element = animation.getElement();

            computedStyle = window.getComputedStyle(element.dom);

            elementId = element.getId();

            previous = runningData[elementId];

            animation = Ext.merge({}, animation.getData());

            data[elementId] = animation;

            before = animation.before;
            from = animation.from;
            to = animation.to;

            animation.fromPropertyNames = fromPropertyNames = [];
            animation.toPropertyNames = toPropertyNames = [];

            for (name in to) {
                if (to.hasOwnProperty(name)) {
                    to[name] = toFormattedValue = this.formatValue(to[name], name);
                    formattedName = this.formatName(name);
                    isLengthProperty = isLengthPropertyMap.hasOwnProperty(name);

                    if (!isLengthProperty) {
                        toFormattedValue = this.getCssStyleValue(formattedName, toFormattedValue);
                    }

                    if (from.hasOwnProperty(name)) {
                        from[name] = fromFormattedValue = this.formatValue(from[name], name);

                        if (!isLengthProperty) {
                            fromFormattedValue = this.getCssStyleValue(formattedName, fromFormattedValue);
                        }

                        if (toFormattedValue !== fromFormattedValue) {
                            fromPropertyNames.push(formattedName);
                            toPropertyNames.push(formattedName);
                        }
                    }
                    else {
                        computedValue = computedStyle.getPropertyValue(formattedName);

                        if (toFormattedValue !== computedValue) {
                            toPropertyNames.push(formattedName);
                        }
                    }
                }
            }

            propertyNames = Ext.Array.merge(fromPropertyNames, toPropertyNames);

            propertiesMap = {};
            propertiesLength = toPropertyNames.length;

            for (j = 0; j < propertiesLength; j++) {
                propertiesMap[toPropertyNames[j]] = true;
            }

            if (!(runningAnimations = runningAnimationsData[elementId])) {
                runningAnimationsData[elementId] = runningAnimations = [];
            }

            this.refreshRunningAnimationsData(element, propertyNames, true);

            if (propertiesLength === 0) {
                this.onAnimationEnd(element, animation);
                continue;
            }
            else {
                runningAnimations.push({
                    element: element,
                    properties: propertiesMap,
                    length: propertiesLength,
                    data: animation
                });
            }

            fromData[elementId] = from = Ext.apply(Ext.Object.chain(before), from);

            if (previous) {
                fromPropertyNames = Ext.Array.difference(previous.toPropertyNames, fromPropertyNames);
                toPropertyNames = Ext.Array.merge(fromPropertyNames, toPropertyNames);

                from['transition-property'] = fromPropertyNames;
            }

            toData[elementId] = to = Ext.Object.chain(to);

            to['transition-property'] = toPropertyNames;
            to['transition-duration'] = animation.duration;
            to['transition-timing-function'] = animation.easing;
            to['transition-delay'] = animation.delay;
        }

        Ext.merge(runningData, data);

        if (requestAnimationFrame) {
            window[requestAnimationFrame](function() {
                me.applyStyles(fromData);
                window[requestAnimationFrame](function() {
                    me.applyStyles(toData);
                });
            });
        }
        else {
            message = this.$className;

            this.applyStyles(fromData);

            doApplyTo = function(e) {
                if (e.data === message && e.source === window) {
                    window.removeEventListener('message', doApplyTo, false);
                    me.applyStyles(toData);
                }
            };

            window.addEventListener('message', doApplyTo, false);
            window.postMessage(message, '*');
        }
    }
});


Ext.define('Ext.fx.Runner', {
    requires: [
        'Ext.fx.runner.CssTransition',
        'Ext.fx.runner.CssAnimation'
    ],

    constructor: function() {
        return new Ext.fx.runner.CssTransition();
    }
});

(function(clsPrefix) {


Ext.define('Ext.layout.Default', {
    extend: 'Ext.EventedBase',
    alternateClassName: ['Ext.layout.AutoContainerLayout', 'Ext.layout.ContainerLayout'],

    alias: ['layout.auto', 'layout.default'],

    isLayout: true,

    eventNames: {
        add: 'add',
        remove: 'remove',
        move: 'move',
        centeredChange: 'centeredchange',
        floatingChange: 'floatingchange',
        dockedChange: 'dockedchange',
        activeItemChange: 'activeitemchange'
    },

    hasDockedItemsCls: clsPrefix + 'hasdocked',

    centeredItemCls: clsPrefix + 'centered',

    floatingItemCls: clsPrefix + 'floating',

    dockingWrapperCls: clsPrefix + 'docking',

    dockingInnerCls: clsPrefix + 'docking-inner',

    maskCls: clsPrefix + 'mask',

    positionMap: {
        top: 'start',
        left: 'start',
        bottom: 'end',
        right: 'end'
    },

    positionDirectionMap: {
        top: 'vertical',
        bottom: 'vertical',
        left: 'horizontal',
        right: 'horizontal'
    },

    DIRECTION_VERTICAL: 'vertical',

    DIRECTION_HORIZONTAL: 'horizontal',

    POSITION_START: 'start',

    POSITION_END: 'end',

    constructor: function(container, config) {
        this.container = container;

        this.innerItems = [];

        this.centeringWrappers = {};

        this.callParent([config]);
    },

    reapply: Ext.emptyFn,

    unapply: Ext.emptyFn,

    onItemAdd: function() {
        this.doItemAdd.apply(this, arguments);
    },

    onItemRemove: function() {
        this.doItemRemove.apply(this, arguments);
    },

    onItemMove: function() {
        this.doItemMove.apply(this, arguments);
    },

    onItemCenteredChange: function() {
        this.doItemCenteredChange.apply(this, arguments);
    },

    onItemFloatingChange: function() {
        this.doItemFloatingChange.apply(this, arguments);
    },

    onItemDockedChange: function() {
        this.doItemDockedChange.apply(this, arguments);
    },

    onActiveItemChange: function() {
        this.doActiveItemChange.apply(this, arguments);
    },

    
    doItemAdd: function(item, index) {
        var dockedPosition = item.getDocked();

        if (dockedPosition) {
            this.dockItem(item, dockedPosition);
        }
        else if (item.isCentered()) {
            this.centerItem(item, index);
        }
        else {
            this.insertItem(item, index);
        }

        if (item.isFloating()) {
            this.onItemFloatingChange(item, true);
        }
    },

    
    doItemRemove: function(item) {
        if (item.isDocked()) {
            this.undockItem(item);
        }
        else if (item.isCentered()) {
            this.uncenterItem(item);
        }

        Ext.Array.remove(this.innerItems, item);

        this.container.innerElement.dom.removeChild(item.renderElement.dom);
    },

    
    doItemMove: function(item, toIndex, fromIndex) {
        if (item.isCentered()) {
            item.setZIndex(toIndex + 100); 
        }
        else {
            this.insertItem(item, toIndex);
        }
    },

    
    doItemCenteredChange: function(item, centered) {
        if (centered) {
            this.centerItem(item);
        }
        else {
            this.uncenterItem(item);
        }
    },

    
    doItemFloatingChange: function(item, floating) {
        var element = item.element,
            floatingItemCls = this.floatingItemCls;

        if (floating) {
            
            if (item.getModal() && !item.getCentered()) {
                this.addModalMask(item);
            }
            item.setZIndex(this.container.indexOf(item) + 100); 
            element.addCls(floatingItemCls);
        }
        else {
            item.setZIndex(null);
            element.removeCls(floatingItemCls);
        }
    },

    
    addModalMask: function(item) {
        var container = this.container,
            modalMask = container.modalMask;

        if (!modalMask) {
            container.modalMask = modalMask = new Ext.Mask();
        }

        
        container.element.append(modalMask.renderElement);

        if (item.isPainted()) {
            modalMask.show();
        }

        if (item.getHideOnMaskTap()) {
            modalMask.on({
                tap: function(mask) {
                    mask.hide();
                    item.hide();
                }
            });
        }

        item.on({
            erased: function() {
                modalMask.hide();
            },
            painted: function() {
                modalMask.show();
            }
        });
    },

    
    doItemDockedChange: function(item, docked, oldDocked) {
        if (oldDocked) {
            this.undockItem(item, oldDocked);
        }

        if (docked) {
            this.dockItem(item, docked);
        }
    },

    doActiveItemChange: Ext.emptyFn,

    centerItem: function(item) {
        this.insertItem(item, 0);
        item.setZIndex(this.container.indexOf(item) + 1);
        this.createCenteringWrapper(item);

        
        item.element.addCls(this.floatingItemCls);
    },

    uncenterItem: function(item) {
        this.destroyCenteringWrapper(item);
        item.setZIndex(null);
        this.insertItem(item, this.container.indexOf(item));

        
        item.element.removeCls(this.floatingItemCls);
    },

    dockItem: function(item, position) {
        var container = this.container,
            itemRenderElement = item.renderElement,
            itemElement = item.element,
            dockingInnerElement = this.dockingInnerElement;

        if (!dockingInnerElement) {
            container.setUseBodyElement(true);
            this.dockingInnerElement = dockingInnerElement = container.bodyElement;
        }

        this.getDockingWrapper(position);

        if (this.positionMap[position] === this.POSITION_START) {
            itemRenderElement.insertBefore(dockingInnerElement);
        }
        else {
            itemRenderElement.insertAfter(dockingInnerElement);
        }

        itemElement.addCls(clsPrefix + 'docked-' + position);
    },

    undockItem: function(item, docked) {
        this.insertItem(item, this.container.indexOf(item));
        item.element.removeCls(clsPrefix + 'docked-' + docked);
    },

    getDockingWrapper: function(position) {
        var currentDockingDirection = this.currentDockingDirection,
            direction = this.positionDirectionMap[position],
            dockingWrapper = this.dockingWrapper;

        if (currentDockingDirection !== direction) {
            this.currentDockingDirection = direction;
            this.dockingWrapper = dockingWrapper = this.createDockingWrapper(direction);
        }

        return dockingWrapper;
    },

    createDockingWrapper: function(direction) {
        return this.dockingInnerElement.wrap({
            classList: [this.dockingWrapperCls + '-' + direction]
        }, true);
    },

    createCenteringWrapper: function(item) {
        var id = item.getId(),
            wrappers = this.centeringWrappers,
            renderElement = item.renderElement,
            maskCls = this.maskCls,
            wrapper;

        wrappers[id] = wrapper = renderElement.wrap({
            className: this.centeredItemCls
        });

        
        
        if (item.getModal()) {
            if (item.getHideOnMaskTap()) {
                wrapper.on({
                    tap: function() {
                        item.hide();
                        wrapper.removeCls(maskCls);
                    }
                });
            }

            item.on({
                painted: function() {
                    wrapper.addCls(maskCls);
                },
                erased: function() {
                    wrapper.removeCls(maskCls);
                }
            })
        }

        return wrapper;
    },

    destroyCenteringWrapper: function(item) {
        var id = item.getId(),
            wrappers = this.centeringWrappers,
            renderElement = item.renderElement,
            wrapper = wrappers[id];

        renderElement.unwrap();
        wrapper.destroy();
        delete wrappers[id];

        return this;
    },

    insertItem: function(item, index) {
       var container = this.container,
           items = container.getItems().items,
           innerItems = this.innerItems,
           containerDom = container.innerElement.dom,
           itemDom = item.renderElement.dom,
           relativeItem, relativeItemDom, domIndex;

       if (container.has(item)) {
           Ext.Array.remove(innerItems, item);
       }

       if (typeof index == 'number') {
           
           relativeItem = items[index];

           
           if (relativeItem === item) {
               relativeItem = items[++index];
           }

           
           while (relativeItem && (relativeItem.isCentered() || relativeItem.isDocked())) {
               relativeItem = items[++index];
           }

           if (relativeItem) {
               
               domIndex = innerItems.indexOf(relativeItem);

               while (relativeItem && (relativeItem.isCentered() || relativeItem.isDocked())) {
                   relativeItem = innerItems[++domIndex];
               }

               if (relativeItem) {
                   innerItems.splice(domIndex, 0, item);

                   relativeItemDom = relativeItem.renderElement.dom;
                   containerDom.insertBefore(itemDom, relativeItemDom);

                   return this;
               }
           }
       }

       innerItems.push(item);
       containerDom.appendChild(itemDom);

       return this;
   }
});

})(Ext.baseCSSPrefix);


Ext.define('Ext.layout.AbstractBox', {
    extend: 'Ext.layout.Default',

    config: {
        
        align: 'stretch',

        
        pack: null
    },

    flexItemCls: Ext.baseCSSPrefix + 'layout-box-item',

    positionMap: {
        middle: 'center',
        left: 'start',
        top: 'start',
        right: 'end',
        bottom: 'end'
    },

    constructor: function(container) {
        this.callParent(arguments);

        this.wrappers = {};

        container.innerElement.addCls(this.cls);

        container.on(this.sizeChangeEventName, 'onItemSizeChange', this, {
            delegate: '> component'
        });
    },

    reapply: function() {
        this.container.innerElement.addCls(this.cls);

        this.updatePack(this.getPack());
        this.updateAlign(this.getAlign());
    },

    unapply: function() {
        this.container.innerElement.removeCls(this.cls);

        this.updatePack(null);
        this.updateAlign(null);
    },

    
    doItemAdd: function(item, index) {
        this.callParent(arguments);

        if (item.isInnerItem()) {
            var size = item.getConfig(this.sizePropertyName),
                config = item.config;

            if (!size && ('flex' in config)) {
                this.setItemFlex(item, config.flex);
            }
        }
    },

    
    doItemRemove: function(item) {
        if (item.isInnerItem()) {
            this.setItemFlex(item, null);
        }

        this.callParent(arguments);
    },

    onItemSizeChange: function(item) {
        this.setItemFlex(item, null);
    },

    
    doItemCenteredChange: function(item, centered) {
        if (centered) {
            this.setItemFlex(item, null);
        }

        this.callParent(arguments);
    },

    
    doItemFloatingChange: function(item, floating) {
        if (floating) {
            this.setItemFlex(item, null);
        }

        this.callParent(arguments);
    },

    
    doItemDockedChange: function(item, docked) {
        if (docked) {
            this.setItemFlex(item, null);
        }

        this.callParent(arguments);
    },

    redrawContainer: function() {
        var container = this.container,
            renderedTo = container.renderElement.dom.parentNode;

        if (renderedTo && renderedTo.nodeType !== 11) {
            container.innerElement.redraw();
        }
    },

    setItemFlex: function(item, flex) {
        var element = item.element,
            flexItemCls = this.flexItemCls;

        if (flex) {
            element.addCls(flexItemCls);
        }
        else if (element.hasCls(flexItemCls)) {
            this.redrawContainer();
            element.removeCls(flexItemCls);
        }

        element.dom.style.webkitBoxFlex = flex;
    },

    convertPosition: function(position) {
        if (this.positionMap.hasOwnProperty(position)) {
            return this.positionMap[position];
        }

        return position;
    },

    applyAlign: function(align) {
        return this.convertPosition(align);
    },

    updateAlign: function(align) {
        this.container.innerElement.dom.style.webkitBoxAlign = align;
    },

    applyPack: function(pack) {
         return this.convertPosition(pack);
    },

    updatePack: function(pack) {
        this.container.innerElement.dom.style.webkitBoxPack = pack;
    }
});


Ext.define('Ext.layout.Fit', {
    extend: 'Ext.layout.Default',
    alternateClassName: 'Ext.layout.FitLayout',

    alias: 'layout.fit',

    cls: Ext.baseCSSPrefix + 'layout-fit',

    constructor: function(container) {
        this.callParent(arguments);

        this.apply();
    },

    apply: function() {
        this.container.innerElement.addCls(this.cls);
    },

    reapply: function() {
        this.apply();
    },

    unapply: function() {
        this.container.innerElement.removeCls(this.cls);
    }
});


Ext.define('Ext.layout.HBox', {
    extend: 'Ext.layout.AbstractBox',
    alternateClassName: 'Ext.layout.HBoxLayout',

    alias: 'layout.hbox',

    sizePropertyName: 'width',

    sizeChangeEventName: 'widthchange',

    cls: Ext.baseCSSPrefix + 'layout-hbox'
});




Ext.define('Ext.layout.VBox', {
    extend: 'Ext.layout.AbstractBox',
    alternateClassName: 'Ext.layout.VBoxLayout',

    alias: 'layout.vbox',

    sizePropertyName: 'height',

    sizeChangeEventName: 'heightchange',

    cls: Ext.baseCSSPrefix + 'layout-vbox'
});
Ext.define('Ext.log.writer.Remote', {
    extend: 'Ext.log.writer.Writer',

    requires: [
        'Ext.Ajax'
    ],

    config: {
        batchSendDelay: 100,
        onFailureRetryDelay: 500,
        url: ''
    },

    isSending: false,

    sendingTimer: null,

    constructor: function() {
        this.queue = [];

        this.send = Ext.Function.bind(this.send, this);

        return this.callParent(arguments);
    },

    doWrite: function(event) {
        var queue = this.queue;
        queue.push(event.message);

        if (!this.isSending && this.sendingTimer === null) {
            this.sendingTimer = setTimeout(this.send, this.getBatchSendDelay());
        }
    },

    send: function() {
        var queue = this.queue,
            messages = queue.slice();

        queue.length = 0;

        this.sendingTimer = null;

        if (messages.length > 0) {
            this.doSend(messages);
        }
    },

    doSend: function(messages) {
        var me = this;

        me.isSending = true;

        Ext.Ajax.request({
            url: me.getUrl(),
            method: 'POST',
            params: {
                messages: messages.join("\n")
            },
            success: function(){
                me.isSending = false;
                me.send();
            },
            failure: function() {
                setTimeout(function() {
                    me.doSend(messages);
                }, me.getOnFailureRetryDelay());
            },
            scope: me
        });
    }
});


Ext.define('Ext.scroll.easing.Bounce', {

    extend: 'Ext.scroll.easing.Easing',

    config: {
        springTension: 0.3,
        acceleration: 30,
        startVelocity: 0
    },

    getValue: function() {
        var deltaTime = Ext.Date.now() - this.getStartTime(),
            theta = (deltaTime / this.getAcceleration()),
            powTime = theta * Math.pow(Math.E, -this.getSpringTension() * theta);

        return this.getStartValue() + (this.getStartVelocity() * powTime);
    }
});


Ext.define('Ext.scroll.easing.BoundMomentum', {
    extend: 'Ext.scroll.easing.Easing',

    requires: [
        'Ext.scroll.easing.Momentum',
        'Ext.scroll.easing.Bounce'
    ],

    config: {
        momentum: null,

        bounce: null,

        minMomentumValue: 0,

        maxMomentumValue: 0,

        minVelocity: 0.01,

        startVelocity: 0
    },

    applyMomentum: function(config, currentEasing) {
        return Ext.factory(config, Ext.scroll.easing.Momentum, currentEasing);
    },

    applyBounce: function(config, currentEasing) {
        return Ext.factory(config, Ext.scroll.easing.Bounce, currentEasing);
    },

    updateStartTime: function(startTime) {
        this.getMomentum().setStartTime(startTime);

        this.callParent(arguments);
    },

    updateStartVelocity: function(startVelocity) {
        this.getMomentum().setStartVelocity(startVelocity);
    },

    updateStartValue: function(startValue) {
        this.getMomentum().setStartValue(startValue);
    },

    reset: function() {
        this.lastValue = null;

        this.isBouncingBack = false;

        this.isOutOfBound = false;

        return this.callParent(arguments);
    },

    getValue: function() {
        var momentum = this.getMomentum(),
            bounce = this.getBounce(),
            startVelocity = momentum.getStartVelocity(),
            direction = startVelocity > 0 ? 1 : -1,
            minValue = this.getMinMomentumValue(),
            maxValue = this.getMaxMomentumValue(),
            boundedValue = (direction == 1) ? maxValue : minValue,
            lastValue = this.lastValue,
            value, velocity;

        if (startVelocity === 0) {
            return this.getStartValue();
        }

        if (!this.isOutOfBound) {
            value = momentum.getValue();
            velocity = momentum.getVelocity();

            if (Math.abs(velocity) < this.getMinVelocity()) {
                this.isEnded = true;
            }

            if (value >= minValue && value <= maxValue) {
                return value;
            }

            this.isOutOfBound = true;

            bounce.setStartTime(Ext.Date.now())
                  .setStartVelocity(velocity)
                  .setStartValue(boundedValue);
        }

        value = bounce.getValue();

        if (!this.isEnded) {
            if (!this.isBouncingBack) {
                if (lastValue !== null) {
                    if ((direction == 1 && value < lastValue) || (direction == -1 && value > lastValue)) {
                        this.isBouncingBack = true;
                    }
                }
            }
            else {
                if (Math.round(value) == boundedValue) {
                    this.isEnded = true;
                }
            }
        }

        this.lastValue = value;

        return value;
    }
});


Ext.define('Ext.scroll.easing.EaseOut', {
    extend: 'Ext.scroll.easing.Linear',

    config: {
        exponent: 4,
        duration: 1500
    },

    getValue: function() {
        var deltaTime = Ext.Date.now() - this.getStartTime(),
            duration = this.getDuration(),
            startValue = this.getStartValue(),
            endValue = this.getEndValue(),
            distance = this.distance,
            theta = deltaTime / duration,
            thetaC = 1 - theta,
            thetaEnd = 1 - Math.pow(thetaC, this.getExponent()),
            currentValue = startValue + (thetaEnd * distance);

        if (deltaTime >= duration) {
            this.isEnded = true;
            return endValue;
        }

        return currentValue;
    }
});


Ext.define('Ext.fx.layout.card.Scroll', {
    extend: 'Ext.fx.layout.card.Abstract',

    requires: [
        'Ext.scroll.easing.EaseOut'
    ],

    alias: 'fx.layout.card.scroll',

    config: {
        duration: 500,

        reverse: null
    },

    constructor: function(config) {
        this.initConfig(config);

        this.doAnimationFrame = Ext.Function.bind(this.doAnimationFrame, this);
    },

    getEasing: function() {
        var easing = this.easing;

        if (!easing) {
            this.easing = easing = new Ext.scroll.easing.EaseOut();
        }

        return easing;
    },

    updateDuration: function(duration) {
        this.getEasing().setDuration(duration + 100);
    },

    onActiveItemChange: function(newItem, oldItem) {
        var containerElement, inElement, outElement, easing,
            containerWidth, reverse;

        if (newItem && oldItem) {
            if (this.isAnimating) {
                this.stopAnimation();
            }

            containerElement = this.getLayout().container.innerElement;
            containerWidth = containerElement.getWidth();
            easing = this.getEasing();

            inElement = newItem.renderElement;
            outElement = oldItem.renderElement;

            this.oldItem = oldItem;
            this.newItem = newItem;
            this.containerElement = containerElement;
            this.isReverse = reverse = this.getReverse();

            newItem.show();

            if (reverse) {
                easing.setConfig({
                    startValue: containerWidth,
                    endValue: 0
                });

                containerElement.dom.scrollLeft = containerWidth;
                outElement.setLeft(containerWidth);
            }
            else {
                easing.setConfig({
                    startValue: 0,
                    endValue: containerWidth
                });

                inElement.setLeft(containerWidth);
            }

            this.startAnimation();

            return false;
        }
    },

    startAnimation: function() {
        this.isAnimating = true;
        this.getEasing().setStartTime(Date.now());
        this.timer = setInterval(this.doAnimationFrame, 20);
        this.doAnimationFrame();
    },

    doAnimationFrame: function() {
        var easing = this.getEasing(),
            value;

        if (easing.isEnded) {
            this.stopAnimation();
        }
        else {
            value = easing.getValue();
            this.containerElement.dom.scrollLeft = value;
        }
    },

    stopAnimation: function() {
        this.oldItem.hide();

        if (this.isReverse) {
            this.oldItem.renderElement.setLeft(null);
        }
        else {
            this.newItem.renderElement.setLeft(null);
        }

        clearInterval(this.timer);
        this.isAnimating = false;
    }
});


Ext.define('Ext.fx.layout.Card', {
    requires: [
        'Ext.fx.layout.card.Slide',
        'Ext.fx.layout.card.Fade',
        'Ext.fx.layout.card.Flip',
        'Ext.fx.layout.card.Pop',
        'Ext.fx.layout.card.Cube',
        'Ext.fx.layout.card.Scroll'
    ],

    constructor: function(config) {
        var defaultClass = Ext.fx.layout.card.Css,
            type;

        if (typeof config == 'string') {
            type = config;

            config = {};
        }
        else if (config.type) {
            type = config.type;
        }

        config.elementBox = false;

        if (type) {
            if (type === 'slide' && Ext.os.is.Android2) {
                type = 'scroll';
            }

            defaultClass = Ext.ClassManager.getByAlias('fx.layout.card.' + type);

            if (!defaultClass) {
                Ext.Logger.error("Unknown card animation type: '" + type + "'");
            }
        }

        return Ext.factory(config, defaultClass);
    }
});


Ext.define('Ext.layout.Card', {
    extend: 'Ext.layout.Fit',
    alternateClassName: 'Ext.layout.CardLayout',

    requires: [
        'Ext.fx.layout.Card'
    ],

    alias: 'layout.card',

    cls: Ext.baseCSSPrefix + 'layout-card',

    itemCls: Ext.baseCSSPrefix + 'layout-card-item',

    config: {
        
        animation: null
    },

    
    applyAnimation: function(animation) {
        return new Ext.fx.layout.Card(animation);
    },

    
    updateAnimation: function(animation, oldAnimation) {
        animation.setLayout(this);

        if (oldAnimation) {
            oldAnimation.destroy();
        }
    },

    
    doItemAdd: function(item, index) {
        if (item.isInnerItem()) {
            item.addCls(this.itemCls);
            item.hide();
        }

        this.callParent(arguments);
    },

    
    doItemRemove: function(item) {
        if (item.isInnerItem()) {
            item.removeCls(this.itemCls);
            item.show();
        }

        this.callParent(arguments);
    },

    
    onActiveItemChange: function(newActiveItem, oldActiveItem) {
        this.fireAction(this.eventNames.activeItemChange, [newActiveItem, oldActiveItem],
            this.doActiveItemChange);
    },

    
    doActiveItemChange: function(newActiveItem, oldActiveItem) {
        if (oldActiveItem) {
            oldActiveItem.hide();
        }

        if (newActiveItem) {
            newActiveItem.show();
        }
    }
});


Ext.define('Ext.layout.Layout', {

    requires: [
        'Ext.layout.Fit',
        'Ext.layout.Card',
        'Ext.layout.HBox',
        'Ext.layout.VBox'
    ],

    
    constructor: function(container, config) {
        var layoutClass = Ext.layout.Default,
            type, layout;

        if (typeof config == 'string') {
            type = config;
            config = {};
        }
        else if ('type' in config) {
            type = config.type;
        }

        if (type) {
            layoutClass = Ext.ClassManager.getByAlias('layout.' + type);

            if (!layoutClass) {
                Ext.Logger.error("Unknown layout type of: '" + type + "'");
            }
        }

        return new layoutClass(container, config);
    }
});



Ext.define('Ext.util.Draggable', {
    isDraggable: true,

    mixins: [
        'Ext.mixin.Observable'
    ],

    config: {
        cls: Ext.baseCSSPrefix + 'draggable',

        draggingCls: Ext.baseCSSPrefix + 'dragging',

        proxyCls: Ext.baseCSSPrefix + 'draggable-proxy',

        element: null,

        constraint: 'container',

        disabled: null,

        
        direction: 'both',

        
        delay: 0,

        
        cancelSelector: null,

        
        revert: false,

        
        group: 'base',

        translateMethod: 'auto',

        initialOffset: 'auto',

        containerConstraint: 'auto',

        autoRefresh: true,

        offset: {
            x: 0,
            y: 0
        }
    },

    CSS_POSITION: 'cssposition',

    CSS_TRANSFORM: 'csstransform',

    DIRECTION_BOTH: 'both',

    DIRECTION_VERTICAL: 'vertical',

    DIRECTION_HORIZONTAL: 'horizontal',

    
    constructor: function(config) {
        var element;

        this.sizeMonitors = {};

        this.initialConfig = config;

        this.listeners = {
            dragstart: 'onDragStart',
            drag     : 'onDrag',
            dragend  : 'onDragEnd',

            scope: this
        };

        this.isAxisEnabledFlags = { x: false, y: false };

        if (config && config.element) {
            element = config.element;
            delete config.element;

            this.setElement(element);
        }

        return this;
    },

    applyElement: function(element) {
        if (!element) {
            this.container = null;
            return;
        }

        return Ext.get(element);
    },

    updateElement: function(element) {
        if (!this.initialized) {
            this.initialized = true;

            this.initConfig(this.initialConfig);

            element.on(this.listeners);

            if (this.getAutoRefresh()) {
                this.sizeMonitors.element = new Ext.util.SizeMonitor({
                    element: element,
                    callback: this.doRefresh,
                    scope: this
                });
            }

            this.onConfigUpdate('containerConstraint', 'refreshConstraint');
            this.onConfigUpdate('constraint', 'refreshOffset');
        }
        else {
            this.refresh();
        }

        element.addCls(this.getCls());

        return this;
    },

    applyConstraint: function(newConstraint, currentConstraint) {
        var initialOffset = this.getInitialOffset(),
            initialOffsetX, initialOffsetY, constraint, containerConstraint;

        this.givenConstraint = newConstraint;

        constraint = {
            min: { x: -Infinity, y: -Infinity },
            max: { x: Infinity, y: Infinity }
        };

        if (newConstraint === 'container') {
            containerConstraint = this.getContainerConstraint();

            initialOffsetX = initialOffset.x;
            initialOffsetY = initialOffset.y;

            constraint.min.x = containerConstraint.min.x - initialOffsetX;
            constraint.min.y = containerConstraint.min.y - initialOffsetY;
            constraint.max.x = containerConstraint.max.x - initialOffsetX;
            constraint.max.y = containerConstraint.max.y - initialOffsetY;
        }
        else if (Ext.isSimpleObject(newConstraint)) {
            Ext.merge(constraint, currentConstraint || {}, newConstraint);
        }

        return constraint;
    },

    applyContainerConstraint: function(newConstraint, currentConstraint) {
        var constraint, container,
            containerDom, dom, containerStyle, min, max,
            width, height, containerWidth, containerHeight,
            paddingTop, paddingLeft, paddingRight, paddingBottom,
            borderTop, borderLeft, borderRight, borderBottom;

        constraint = {
            min: { x: -Infinity, y: -Infinity },
            max: { x: Infinity, y: Infinity }
        };

        this.givenContainerConstraint = newConstraint;

        if (newConstraint === 'auto' && (container = this.getContainer())) {
            dom = this.getElement().dom;
            containerDom = container.dom;

            containerStyle = window.getComputedStyle(containerDom);

            min = constraint.min;
            max = constraint.max;

            width = dom.offsetWidth;
            height = dom.offsetHeight;

            paddingLeft   = this.getNumberValue(containerStyle.paddingLeft);
            paddingTop    = this.getNumberValue(containerStyle.paddingTop);
            paddingRight  = this.getNumberValue(containerStyle.paddingRight);
            paddingBottom = this.getNumberValue(containerStyle.paddingBottom);

            borderTop     = this.getNumberValue(containerStyle.borderTopWidth);
            borderLeft    = this.getNumberValue(containerStyle.borderLeftWidth);
            borderRight   = this.getNumberValue(containerStyle.borderRightWidth);
            borderBottom  = this.getNumberValue(containerStyle.borderBottomWidth);

            containerWidth = containerDom.offsetWidth;
            containerHeight = containerDom.offsetHeight;

            min.x = paddingLeft;
            min.y = paddingTop;

            max.x = Math.max(min.x, containerWidth - paddingRight - borderLeft - borderRight - width);
            max.y = Math.max(min.y, containerHeight - paddingBottom - borderTop - borderBottom - height);
        }
        else if (Ext.isSimpleObject(newConstraint)) {
            Ext.merge(constraint, currentConstraint || {}, newConstraint);
        }

        return constraint;
    },

    getNumberValue: function(value) {
        value = parseInt(value, 10);

        if (isNaN(value)) {
            value = 0;
        }

        return value;
    },

    applyTranslateMethod: function(method) {
        var cssPosition = this.CSS_POSITION,
            cssTransform = this.CSS_TRANSFORM;

        if (method === 'auto') {
            if (Ext.os.is.Android2) {
                method = cssPosition;
            }
            else {
                method = cssTransform;
            }
        }

        if (method !== cssPosition && method !== cssTransform) {
            method = cssTransform;
        }

        return method;
    },

    updateTranslateMethod: function(newMethod, oldMethod) {
        if (oldMethod) {
            if (oldMethod === this.CSS_TRANSFORM) {
                this.translateWithCssTransform(0, 0);
            }
            else {
                this.translateWithCssPosition(0, 0);
            }
        }

        this.updateOffset(this.getOffset());
    },

    getContainer: function() {
        var container = this.container;

        if (!container) {
            container = this.getElement().getParent();

            if (container) {
                if (this.getAutoRefresh()) {
                    this.sizeMonitors.container = new Ext.util.SizeMonitor({
                        element: container,
                        callback: this.doRefresh,
                        scope: this
                    });
                }

                this.container = container;
            }
        }

        return container;
    },

    applyInitialOffset: function(offset) {
        var dom, style;

        if (offset === 'auto') {
            dom = this.getElement().dom;
            style = window.getComputedStyle(dom);

            offset = {
                x: dom.offsetLeft - this.getNumberValue(style.marginLeft),
                y: dom.offsetTop - this.getNumberValue(style.marginTop)
            };
        }

        return offset;
    },

    detachListeners: function() {
        this.getElement().un(this.listeners);
    },

    updateDirection: function(direction) {
        var isAxisEnabled = this.isAxisEnabledFlags;

        isAxisEnabled.x = (direction === this.DIRECTION_BOTH || direction === this.DIRECTION_HORIZONTAL);
        isAxisEnabled.y = (direction === this.DIRECTION_BOTH || direction === this.DIRECTION_VERTICAL);
    },

    isAxisEnabled: function(axis) {
        this.getDirection();

        return this.isAxisEnabledFlags[axis];
    },

    onDragStart: function(e) {
        if (this.getDisabled()) {
            return false;
        }

        this.fireAction('dragstart', [this, e, this.getOffset()], 'initDragStart');
    },

    initDragStart: function(me, e, startOffset) {
        this.dragStartOffset = startOffset;

        this.isDragging = true;

        this.getElement().addCls(this.getDraggingCls());
    },

    onDrag: function(e) {
        if (!this.isDragging) {
            return;
        }

        var startOffset = this.dragStartOffset;

        this.setOffset({
            x: startOffset.x + e.deltaX,
            y: startOffset.y + e.deltaY
        });

        this.fireEvent('drag', this, e, this.getOffset());
    },

    onDragEnd: function(e) {
        if (!this.isDragging) {
            return;
        }

        this.onDrag(e);

        this.isDragging = false;

        this.fireEvent('dragend', this, e);

        this.getElement().removeCls(this.getDraggingCls());
    },

    applyOffset: function(offset, oldOffset) {
        var x = offset.x,
            y = offset.y,
            constraint = this.getConstraint(),
            minOffset = constraint.min,
            maxOffset = constraint.max,
            min = Math.min,
            max = Math.max;

        if (this.isAxisEnabled('x') && typeof x == 'number') {
            x = min(max(x, minOffset.x), maxOffset.x);
        }
        else {
            x = oldOffset ? oldOffset.x : 0;
        }

        if (this.isAxisEnabled('y') && typeof y == 'number') {
            y = min(max(y, minOffset.y), maxOffset.y);
        }
        else {
            y = oldOffset ? oldOffset.y : 0;
        }

        return {
            x: x,
            y: y
        };
    },

    updateOffset: function(offset, oldOffset) {
        var x = offset.x,
            y = offset.y;

        if (!oldOffset || x !== oldOffset.x || y !== oldOffset.y) {
            this.moveTo(x, y);
        }
    },

    moveTo: function(x, y) {
        var method = this.getTranslateMethod();

        if (method === this.CSS_POSITION) {
            return this.translateWithCssPosition.apply(this, arguments);
        }
        else {
            return this.translateWithCssTransform.apply(this, arguments);
        }
    },

    translateWithCssPosition: function(x, y) {
        var initialOffset = this.getInitialOffset(),
            domStyle = this.getElement().dom.style;

        domStyle.left = (x + initialOffset.x) + 'px';
        domStyle.top = (y + initialOffset.y) + 'px';
    },

    translateWithCssTransform: function(x, y) {
        var domStyle = this.getElement().dom.style;

        domStyle.webkitTransform = 'translate3d(' + x + 'px, ' + y + 'px, 0px)';
    },

    refreshConstraint: function() {
        this.setConstraint(this.givenConstraint);
    },

    refreshOffset: function() {
        this.setOffset(this.getOffset());
    },

    doRefresh: function() {
        this.setContainerConstraint(this.givenContainerConstraint);
        this.refreshConstraint();
        this.refreshOffset();
    },

    refresh: function() {
        var sizeMonitors = this.sizeMonitors;

        if (sizeMonitors.element) { 
            sizeMonitors.element.refresh();
        }
        
        if (sizeMonitors.container) {
            sizeMonitors.container.refresh();
        }

        this.doRefresh();
    },

    
    enable: function() {
        return this.setDisabled(false);
    },

    
    disable: function() {
        return this.setDisabled(true);
    },

    destroy: function() {
        var sizeMonitors = this.sizeMonitors;

        if (sizeMonitors.element) {
            sizeMonitors.element.destroy();
        }

        if (sizeMonitors.container) {
            sizeMonitors.container.destroy();
        }

        this.getElement().removeCls(this.getCls());
        this.detachListeners();
    }

}, function() {
    this.override({
        constructor: function(config) {
            if (config && config.constrain) {
                Ext.Logger.deprecate("'constrain' config is deprecated, please use 'contraint' instead");
                config.contraint = config.constrain;
                delete config.constrain;
            }

            return this.callOverridden(arguments);
        }
    });
});



Ext.define('Ext.Sortable', {
    mixins: {
        observable: 'Ext.mixin.Observable'
    },

    requires: ['Ext.util.Draggable'],

    config: {
        
        baseCls: Ext.baseCSSPrefix + 'sortable',

        
        delay: 0

        },

    
    direction: 'vertical',

    
    cancelSelector: null,

    
    
    
    

    
    constrain: window,
    
    group: 'base',

    
    revert: true,

    
    itemSelector: null,

    
    handleSelector: null,

    
    disabled: false,

    

    
    sorting: false,

    
    vertical: false,

    
    vertical: false,

    
    constructor : function(el, config) {
        config = config || {};
        Ext.apply(this, config);

        this.addEvents(
            
            'sortstart',
            
            'sortend',
            
            'sortchange'

            
            
            
            
            
            
            
            
        );

        this.el = Ext.get(el);
        this.callParent();

        this.mixins.observable.constructor.call(this);

        if (this.direction == 'horizontal') {
            this.horizontal = true;
        }
        else if (this.direction == 'vertical') {
            this.vertical = true;
        }
        else {
            this.horizontal = this.vertical = true;
        }

        this.el.addCls(this.baseCls);
        this.startEventName = (this.getDelay() > 0) ? 'taphold' : 'tapstart';
        if (!this.disabled) {
            this.enable();
        }
    },

    
    onStart : function(e, t) {
        if (this.cancelSelector && e.getTarget(this.cancelSelector)) {
            return;
        }
        if (this.handleSelector && !e.getTarget(this.handleSelector)) {
            return;
        }

        if (!this.sorting) {
            this.onSortStart(e, t);
        }
    },

    
    onSortStart : function(e, t) {
        this.sorting = true;
        var draggable = Ext.create('Ext.util.Draggable', t, {
            threshold: 0,
            revert: this.revert,
            direction: this.direction,
            constrain: this.constrain === true ? this.el : this.constrain,
            animationDuration: 100
        });
        draggable.on({
            drag: this.onDrag,
            dragend: this.onDragEnd,
            scope: this
        });

        this.dragEl = t;
        this.calculateBoxes();

        if (!draggable.dragging) {
            draggable.onStart(e);
        }

        this.fireEvent('sortstart', this, e);
    },

    
    calculateBoxes : function() {
        this.items = [];
        var els = this.el.select(this.itemSelector, false),
            ln = els.length, i, item, el, box;

        for (i = 0; i < ln; i++) {
            el = els[i];
            if (el != this.dragEl) {
                item = Ext.fly(el).getPageBox(true);
                item.el = el;
                this.items.push(item);
            }
        }
    },

    
    onDrag : function(draggable, e) {
        var items = this.items,
            ln = items.length,
            region = draggable.region,
            sortChange = false,
            i, intersect, overlap, item;

        for (i = 0; i < ln; i++) {
            item = items[i];
            intersect = region.intersect(item);
            if (intersect) {
                if (this.vertical && Math.abs(intersect.top - intersect.bottom) > (region.bottom - region.top) / 2) {
                    if (region.bottom > item.top && item.top > region.top) {
                        draggable.el.insertAfter(item.el);
                    }
                    else {
                        draggable.el.insertBefore(item.el);
                    }
                    sortChange = true;
                }
                else if (this.horizontal && Math.abs(intersect.left - intersect.right) > (region.right - region.left) / 2) {
                    if (region.right > item.left && item.left > region.left) {
                        draggable.el.insertAfter(item.el);
                    }
                    else {
                        draggable.el.insertBefore(item.el);
                    }
                    sortChange = true;
                }

                if (sortChange) {
                    
                    draggable.reset();

                    
                    
                    draggable.moveTo(region.left, region.top);

                    
                    this.calculateBoxes();
                    this.fireEvent('sortchange', this, draggable.el, this.el.select(this.itemSelector, false).indexOf(draggable.el.dom));
                    return;
                }
            }
        }
    },

    
    onDragEnd : function(draggable, e) {
        draggable.destroy();
        this.sorting = false;
        this.fireEvent('sortend', this, draggable, e);
    },

    
    enable : function() {
        this.el.on(this.startEventName, this.onStart, this, {delegate: this.itemSelector, holdThreshold: this.getDelay()});
        this.disabled = false;
    },

    
    disable : function() {
        this.el.un(this.startEventName, this.onStart, this);
        this.disabled = true;
    },

    
    isDisabled: function() {
        return this.disabled;
    },

    
    isSorting : function() {
        return this.sorting;
    },

    
    isVertical : function() {
        return this.vertical;
    },

    
    isHorizontal : function() {
        return this.horizontal;
    }
});
Ext.define('Ext.behavior.Draggable', {

    extend: 'Ext.behavior.Behavior',

    requires: [
        'Ext.util.Draggable'
    ],

    constructor: function() {
        this.listeners = {
            painted: 'onComponentPainted',
            scope: this
        };

        this.callParent(arguments);
    },

    onComponentPainted: function() {
        this.draggable.refresh();
    },

    setConfig: function(config) {
        var draggable = this.draggable,
            component = this.component;

        if (config) {
            if (!draggable) {
                this.draggable = draggable = new Ext.util.Draggable(config);
                draggable.setElement(component.renderElement);
                draggable.on('destroy', 'onDraggableDestroy', this);

                if (component.isPainted()) {
                    this.onComponentPainted(component);
                }

                component.on(this.listeners);
            }
            else if (Ext.isObject(config)) {
                draggable.setConfig(config);
            }
        }
        else if (draggable) {
            draggable.destroy();
        }
    },

    getDraggable: function() {
        return this.draggable;
    },

    onDraggableDestroy: function() {
        var component = this.component;

        component.un(this.listeners);
    }
});


Ext.define('Ext.util.Droppable', {
    mixins: {
        observable: 'Ext.util.Observable'
    },

    config: {
        
        baseCls: Ext.baseCSSPrefix + 'droppable'
    },

    
    activeCls: Ext.baseCSSPrefix + 'drop-active',

    
    invalidCls: Ext.baseCSSPrefix + 'drop-invalid',

    
    hoverCls: Ext.baseCSSPrefix + 'drop-hover',

    
    validDropMode: 'intersect',

    
    disabled: false,

    
    group: 'base',

    
    tolerance: null,

    
    monitoring: false,

    
    constructor: function(el, config) {
        var me = this;

        config = config || {};
        Ext.apply(me, config);

        

        

        

        

        

        me.el = Ext.get(el);
        me.callParent();

        me.mixins.observable.constructor.call(me);

        if (!me.disabled) {
            me.enable();
        }

        me.el.addCls(me.baseCls);
    },

    
    onDragStart: function(draggable, e) {
        if (draggable.group === this.group) {
            this.monitoring = true;
            this.el.addCls(this.activeCls);
            this.region = this.el.getPageBox(true);

            draggable.on({
                drag: this.onDrag,
                beforedragend: this.onBeforeDragEnd,
                dragend: this.onDragEnd,
                scope: this
            });

            if (this.isDragOver(draggable)) {
                this.setCanDrop(true, draggable, e);
            }

            this.fireEvent('dropactivate', this, draggable, e);
        }
        else {
            draggable.on({
                dragend: function() {
                    this.el.removeCls(this.invalidCls);
                },
                scope: this,
                single: true
            });
            this.el.addCls(this.invalidCls);
        }
    },

    
    isDragOver: function(draggable, region) {
        return this.region[this.validDropMode](draggable.region);
    },

    
    onDrag: function(draggable, e) {
        this.setCanDrop(this.isDragOver(draggable), draggable, e);
    },

    
    setCanDrop: function(canDrop, draggable, e) {
        if (canDrop && !this.canDrop) {
            this.canDrop = true;
            this.el.addCls(this.hoverCls);
            this.fireEvent('dropenter', this, draggable, e);
        }
        else if (!canDrop && this.canDrop) {
            this.canDrop = false;
            this.el.removeCls(this.hoverCls);
            this.fireEvent('dropleave', this, draggable, e);
        }
    },

    
    onBeforeDragEnd: function(draggable, e) {
        draggable.cancelRevert = this.canDrop;
    },

    
    onDragEnd: function(draggable, e) {
        this.monitoring = false;
        this.el.removeCls(this.activeCls);

        draggable.un({
            drag: this.onDrag,
            beforedragend: this.onBeforeDragEnd,
            dragend: this.onDragEnd,
            scope: this
        });


        if (this.canDrop) {
            this.canDrop = false;
            this.el.removeCls(this.hoverCls);
            this.fireEvent('drop', this, draggable, e);
        }

        this.fireEvent('dropdeactivate', this, draggable, e);
    },

    
    enable: function() {
        if (!this.mgr) {
            this.mgr = Ext.util.Observable.observe(Ext.util.Draggable);
        }
        this.mgr.on({
            dragstart: this.onDragStart,
            scope: this
        });
        this.disabled = false;
    },

    
    disable: function() {
        this.mgr.un({
            dragstart: this.onDragStart,
            scope: this
        });
        this.disabled = true;
    },

    
    isDisabled: function() {
        return this.disabled;
    },

    
    isMonitoring: function() {
        return this.monitoring;
    }
});


Ext.define('Ext.util.GeoLocation', {

    mixins: ['Ext.mixin.Observable'],

    config: {
        

        

        
        autoUpdate: true,

        
        latitude: null,

        
        longitude: null,

        
        accuracy: null,

        
        altitude: null,

        
        altitudeAccuracy: null,

        
        heading: null,

        
        speed: null,

        
        timestamp: null,

        
        
        allowHighAccuracy: false,

        

        timeout: Infinity,

        
        maximumAge: 0,

        
        provider : undefined
    },

    updateMaximumAge: function() {
        if (this.watchOperation) {
            this.updateWatchOperation();
        }
    },

    updateTimeout: function() {
        if (this.watchOperation) {
            this.updateWatchOperation();
        }
    },

    updateAllowHighAccuracy: function() {
        if (this.watchOperation) {
            this.updateWatchOperation();
        }
    },

    applyProvider: function(config) {
        if (Ext.feature.has.Geolocation) {
            if (!config) {
                if (navigator && navigator.geolocation) {
                    config = navigator.geolocation;
                }
                else if (window.google) {
                    config = google.gears.factory.create('beta.geolocation');
                }
            }
        }
        return config;
    },

    updateAutoUpdate: function(newAutoUpdate, oldAutoUpdate) {
        var me = this,
            provider = me.getProvider();

        if (oldAutoUpdate && provider) {
            provider.clearWatch(me.watchOperation);
            me.watchOperation = null;
        }

        if (newAutoUpdate) {
            if (!provider) {
                me.fireEvent('locationerror', me, false, false, true, null);
                return;
            }

            try {
                me.updateWatchOperation();
            }
            catch(e) {
                me.fireEvent('locationerror', me, false, false, true, e.message);
            }
        }
    },

    
    updateWatchOperation: function() {
        var me = this,
            provider = me.getProvider();

        if (me.watchOperation) {
            provider.clearWatch(me.watchOperation);
        }

        me.watchOperation = provider.watchPosition(
            Ext.createDelegate(me.fireUpdate, me),
            Ext.createDelegate(me.fireError, me),
            me.parseOptions()
        );
    },

    
    updateLocation: function(callback, scope, positionOptions) {
        var me = this,
            provider = me.getProvider();

        var failFunction = function(message, error) {
            if (error) {
                me.fireError(error);
            }
            else {
                me.fireEvent('locationerror', me, false, false, true, message);
            }
            if (callback) {
                callback.call(scope || me, null, me); 
            }
        };

        if (!provider) {

                failFunction(null);

            return;
        }

        try {
            provider.getCurrentPosition(
                
                function(position) {
                    me.fireUpdate(position);
                    if (callback) {
                        callback.call(scope || me, me, me); 
                    }
                },
                
                function(error) {
                    failFunction(null, error);
                },
                positionOptions || me.parseOptions()
            );
        }
        catch(e) {

                failFunction(e.message);

        }
    },

    
    fireUpdate: function(position) {
        var me = this,
            coords = position.coords;

        me.setConfig({
            timestamp: position.timestamp,
            latitude: coords.latitude,
            longitude: coords.longitude,
            accuracy: coords.accuracy,
            altitude: coords.altitude,
            altitudeAccuracy: coords.altitudeAccuracy,
            heading: coords.heading,
            speed: coords.speed
        });

        me.fireEvent('locationupdate', me);
    },

    
    fireError: function(error) {
        var errorCode = error.code;
        this.fireEvent('locationerror', this,
            errorCode == error.TIMEOUT,
            errorCode == error.PERMISSION_DENIED,
            errorCode == error.POSITION_UNAVAILABLE,
            error.message == undefined ? null : error.message
        );
    },

    
    parseOptions: function() {
        var timeout = this.getTimeout(),
            ret = {
                maximumAge: this.getMaximumAge(),
                allowHighAccuracy: this.getAllowHighAccuracy()
            };

        
        
        if (timeout !== Infinity) {
            ret.timeout = timeout;
        }
        return ret;
    }
});
Ext.define('Ext.util.SizeMonitor', {

    extend: 'Ext.EventedBase',

    config: {
        element: null,

        detectorCls: Ext.baseCSSPrefix + 'size-change-detector',

        callback: Ext.emptyFn,

        scope: null
    },

    initialize: function() {
        this.doFireSizeChangeEvent = Ext.Function.bind(this.doFireSizeChangeEvent, this);

        var me = this,
            element = this.getElement().dom,
            cls = this.getDetectorCls(),
            expandDetector = Ext.Element.create({
                classList: [cls, cls + '-expand'],
                children: [{}]
            }, true),
            shrinkDetector = Ext.Element.create({
                classList: [cls, cls + '-shrink'],
                children: [{}]
            }, true),
            expandListener = function(e) {
                me.onDetectorScroll('expand', e);
            },
            shrinkListener = function(e) {
                me.onDetectorScroll('shrink', e);
            };

        element.appendChild(expandDetector);
        element.appendChild(shrinkDetector);

        expandDetector.addEventListener('scroll', expandListener, true);
        shrinkDetector.addEventListener('scroll', shrinkListener, true);

        this.detectors = {
            expand: expandDetector,
            shrink: shrinkDetector
        };

        this.position = {
            expand: {
                left: 0,
                top: 0
            },
            shrink: {
                left: 0,
                top: 0
            }
        };

        this.listeners = {
            expand: expandListener,
            shrink: shrinkListener
        };

        this.refresh();
    },

    applyElement: function(element) {
        if (element) {
            return Ext.get(element);
        }
    },

    refreshPosition: function(name) {
        var detector = this.detectors[name],
            position = this.position[name],
            left, top;

        position.left = left = detector.scrollWidth - detector.offsetWidth;
        position.top = top = detector.scrollHeight - detector.offsetHeight;

        detector.scrollLeft = left;
        detector.scrollTop = top;
    },

    refresh: function() {
        this.refreshPosition('expand');
        this.refreshPosition('shrink');
    },

    onDetectorScroll: function(name) {
        var detector = this.detectors[name],
            position = this.position[name];
        
        if (detector.scrollLeft !== position.left || detector.scrollTop !== position.top) {
            this.refresh();
            this.fireSizeChangeEvent();
        }
    },

    fireSizeChangeEvent: function() {
        clearTimeout(this.sizeChangeThrottleTimer);

        this.sizeChangeThrottleTimer = setTimeout(this.doFireSizeChangeEvent, 1);
    },

    doFireSizeChangeEvent: function() {
        var callback = this.getCallback(),
            scope = this.getScope();
            
        callback.call(scope);
    },

    destroyDetector: function(name) {
        var detector = this.detectors[name],
            listener = this.listeners[name];

        detector.removeEventListener('scroll', listener, true);
        Ext.removeNode(detector);
    },

    destroy: function() {
        this.callParent(arguments);

        this.destroyDetector('expand');
        this.destroyDetector('shrink');

        delete this.listeners;
        delete this.detectors;
    }
});


Ext.define('Ext.scroll.scroller.Abstract', {

    requires: [
        'Ext.scroll.easing.BoundMomentum',
        'Ext.scroll.easing.EaseOut',
        'Ext.util.SizeMonitor'
    ],

    mixins: [
        'Ext.mixin.Observable'
    ],

    config: {
        element: null,

        
        direction: 'auto',

        momentumEasing: {
            momentum: {
                acceleration: 30,
                friction: 0.5
            },

            bounce: {
                acceleration: 30,
                springTension: 0.3
            },

            minVelocity: 0.2
        },

        snapEasing: {
            duration: 400,
            exponent: 4
        },

        outOfBoundRestrictFactor: 0.5,

        startMomentumResetTime: 300,

        fps: 60,

        maxAbsoluteVelocity: 2,

        containerSize: 'auto',

        containerScrollSize: 'auto',

        size: 'auto',

        snap: null,

        snapOffset: {
            x: 0,
            y: 0
        },

        disabled: null,

        autoRefresh: true,

        cls: Ext.baseCSSPrefix + 'scroll-scroller',

        containerCls: Ext.baseCSSPrefix + 'scroll-container'
    },

    dragStartTime: 0,

    dragEndTime: 0,

    activeEasing: null,

    isDragging: false,

    isAnimating: false,

    constructor: function(config) {
        var element = config && config.element;

        this.doAnimationFrame = Ext.Function.bind(this.doAnimationFrame, this);

        this.listeners = {
            scope: this,
            touchstart: 'onTouchStart',
            dragstart : 'onDragStart',
            drag      : 'onDrag',
            dragend   : 'onDragEnd'
        };

        this.startPosition = { x: 0, y: 0 };

        this.size = { x: 0, y: 0 };

        this.position = { x: 0, y: 0 };

        this.velocity = { x: 0, y: 0 };

        this.isAxisEnabledFlags = { x: false, y: false };

        this.activeEasing = { x: null, y: null };

        this.flickStartPosition = { x: 0, y: 0 };

        this.flickStartTime = { x: 0, y: 0 };

        this.lastDragPosition = { x: 0, y: 0 };

        this.dragDirection = { x: 0, y: 0};

        this.initialConfig = config;

        if (element) {
            delete config.element;
            this.initElement(element);
        }

        return this;
    },

    applyElement: function(element) {
        if (!element) {
            return;
        }

        return Ext.get(element);
    },

    updateElement: function(element) {
        element.addCls(this.getCls());

        this.initConfig(this.initialConfig);

        this.onAfterInitialized();

        return this;
    },

    onAfterInitialized: function() {
        if (!this.getDisabled()) {
            this.attachListeneners();
        }

        this.onConfigUpdate(['containerSize', 'size'], 'refreshMaxPosition');

        this.on('maxpositionchange', 'snapToBoundary');
    },

    attachListeneners: function() {
        this.getContainer().on(this.listeners);
    },

    detachListeners: function() {
        this.getContainer().un(this.listeners);
    },

    updateDisabled: function(disabled) {
        if (disabled) {
            this.detachListeners();
        }
        else {
            this.attachListeneners();
        }
    },

    updateFps: function(fps) {
        this.animationInterval = 1000 / fps;
    },

    applyDirection: function(direction) {
        var maxPosition = this.getMaxPosition(),
            isHorizontal, isVertical;

        this.givenDirection = direction;

        if (direction === 'auto') {
            isHorizontal = maxPosition.x > 0;
            isVertical = maxPosition.y > 0;

            if (isHorizontal && isVertical) {
                direction = 'both';
            }
            else if (isHorizontal) {
                direction = 'horizontal';
            }
            else {
                direction = 'vertical';
            }
        }

        return direction;
    },

    updateDirection: function(direction) {
        var isAxisEnabled = this.isAxisEnabledFlags;

        isAxisEnabled.x = (direction === 'both' || direction === 'horizontal');
        isAxisEnabled.y = (direction === 'both' || direction === 'vertical');
    },

    isAxisEnabled: function(axis) {
        this.getDirection();

        return this.isAxisEnabledFlags[axis];
    },

    applyMomentumEasing: function(easing) {
        var defaultEasingClass = Ext.scroll.easing.BoundMomentum;

        if (!(easing instanceof Ext.scroll.easing.Easing)) {
            return {
                x: new defaultEasingClass(easing),
                y: new defaultEasingClass(easing)
            };
        }

        return {
            x: easing,
            y: easing.clone()
        };
    },

    applySnapEasing: function(easing) {
        var defaultEasingClass = Ext.scroll.easing.EaseOut;

        if (!(easing instanceof Ext.scroll.easing.Easing)) {
            return {
                x: new defaultEasingClass(easing),
                y: new defaultEasingClass(easing)
            };
        }

        return {
            x: easing,
            y: easing.clone()
        };
    },

    getMaxPosition: function() {
        var maxPosition = this.maxPosition,
            size, containerSize;

        if (!maxPosition) {
            size = this.getSize();
            containerSize = this.getContainerSize();

            this.maxPosition = maxPosition = {
                x: Math.max(0, size.x - containerSize.x),
                y: Math.max(0, size.y - containerSize.y)
            };

            this.fireEvent('maxpositionchange', this, maxPosition);
        }

        return maxPosition;
    },

    refreshMaxPosition: function() {
        this.maxPosition = null;
        this.getMaxPosition();
    },

    applyContainerSize: function(size) {
        var containerDom, x, y;

        this.givenContainerSize = size;

        if (size === 'auto') {
            containerDom = this.getContainer().dom;

            x = containerDom.offsetWidth;
            y = containerDom.offsetHeight;
        }
        else {
            x = size.x;
            y = size.y;
        }

        return {
            x: x,
            y: y
        };
    },

    applySize: function(size) {
        var dom, x, y;

        this.givenSize = size;

        if (size === 'auto') {
            dom = this.getElement().dom;

            x = dom.offsetWidth;
            y = dom.offsetHeight;
        }
        else {
            x = size.x;
            y = size.y;
        }

        return {
            x: x,
            y: y
        };
    },

    applyContainerScrollSize: function(size) {
        var containerDom, x, y;

        this.givenContainerScrollSize = size;

        if (size === 'auto') {
            containerDom = this.getContainer().dom;

            x = containerDom.scrollWidth;
            y = containerDom.scrollHeight;
        }
        else {
            x = size.x;
            y = size.y;
        }

        return {
            x: x,
            y: y
        };
    },

    getContainer: function() {
        var container = this.container;

        if (!container) {
            container = this.container = this.getElement().getParent();
            container.addCls(this.getContainerCls());
        }

        return container;
    },

    updateAutoRefresh: function(autoRefresh) {
        var SizeMonitor = Ext.util.SizeMonitor;

        if (autoRefresh) {
            this.sizeMonitors = {
                element: new SizeMonitor({
                    element: this.getElement(),
                    callback: this.doRefresh,
                    scope: this
                }),
                container: new SizeMonitor({
                    element: this.getContainer(),
                    callback: this.doRefresh,
                    scope: this
                })
            };
        }
    },

    doRefresh: function() {
        this.stopAnimation();

        this.setSize(this.givenSize);
        this.setContainerSize(this.givenContainerSize);
        this.setContainerScrollSize(this.givenContainerScrollSize);
        this.setDirection(this.givenDirection);

        this.fireEvent('refresh');
    },

    refresh: function() {
        var sizeMonitors = this.sizeMonitors;

        if (sizeMonitors) {
            sizeMonitors.element.refresh();
            sizeMonitors.container.refresh();
        }

        this.doRefresh();
    },

    scrollTo: function(x, y) {
        if (typeof x != 'number' && arguments.length === 1) {
            y = x.y;
            x = x.x;
        }

        var position = this.position,
            positionChanged = false,
            actualX = null,
            actualY = null;

        if (this.isAxisEnabled('x')) {
            if (typeof x != 'number') {
                x = position.x;
            }
            else {
                if (position.x !== x) {
                    position.x = x;
                    positionChanged = true;
                }
            }

            actualX = x;
        }

        if (this.isAxisEnabled('y')) {
            if (typeof y != 'number') {
                y = position.y;
            }
            else {
                if (position.y !== y) {
                    position.y = y;
                    positionChanged = true;
                }
            }

            actualY = y;
        }

        if (positionChanged) {
            this.fireEvent('scroll', this, position.x, position.y);
            this.doScrollTo(actualX, actualY);
        }

        return this;
    },

    doScrollTo: function(x, y) {
        var containerDom = this.getContainer().dom;

        if (x !== null) {
            containerDom.scrollLeft = x;
        }

        if (y !== null) {
            containerDom.scrollTop = y;
        }
    },

    onTouchStart: function() {
        this.stopAnimation();
    },

    onDragStart: function() {
        this.stopAnimation(true);

        var startPosition = this.startPosition,
            flickStartPosition = this.flickStartPosition,
            flickStartTime = this.flickStartTime,
            lastDragPosition = this.lastDragPosition,
            currentPosition = this.position,
            dragDirection = this.dragDirection,
            x = currentPosition.x,
            y = currentPosition.y,
            now = Ext.Date.now();

        lastDragPosition.x = x;
        lastDragPosition.y = y;

        flickStartPosition.x = x;
        flickStartPosition.y = y;

        startPosition.x = x;
        startPosition.y = y;

        flickStartTime.x = now;
        flickStartTime.y = now;

        dragDirection.x = 0;
        dragDirection.y = 0;

        this.dragStartTime = now;

        this.isDragging = true;

        this.fireEvent('scrollstart');
    },

    onAxisDrag: function(axis, delta) {
        if (!this.isAxisEnabled(axis)) {
            return;
        }

        var flickStartPosition = this.flickStartPosition,
            flickStartTime = this.flickStartTime,
            lastDragPosition = this.lastDragPosition,
            dragDirection = this.dragDirection,
            old = this.position[axis],
            max = this.getMaxPosition()[axis],
            start = this.startPosition[axis],
            last = lastDragPosition[axis],
            current = start - delta,
            lastDirection = dragDirection[axis],
            restrictFactor = this.getOutOfBoundRestrictFactor(),
            startMomentumResetTime = this.getStartMomentumResetTime(),
            now = Ext.Date.now(),
            distance;

        if (current < 0) {
            current *= restrictFactor;
        }
        else if (current > max) {
            distance = current - max;
            current = max + distance * restrictFactor;
        }

        if (current > last) {
            dragDirection[axis] = 1;
        }
        else if (current < last) {
            dragDirection[axis] = -1;
        }

        if ((lastDirection !== 0 && (dragDirection[axis] !== lastDirection)) || (now - flickStartTime[axis]) > startMomentumResetTime) {
            flickStartPosition[axis] = old;
            flickStartTime[axis] = now;
        }

        lastDragPosition[axis] = current;
    },

    onDrag: function(e) {
        if (!this.isDragging) {
            return;
        }

        var lastDragPosition = this.lastDragPosition;

        this.onAxisDrag('x', e.deltaX);
        this.onAxisDrag('y', e.deltaY);

        this.scrollTo(lastDragPosition.x, lastDragPosition.y);
    },

    onDragEnd: function(e) {
        var animationX, animationY;

        if (!this.isDragging) {
            return;
        }

        this.dragEndTime = Ext.Date.now();

        this.onDrag(e);

        this.isDragging = false;

        animationX = this.prepareAnimation('x');
        animationY = this.prepareAnimation('y');

        if (!(animationX === false || animationY === false)) {
            this.isScrolling = true;
        }

        this.startAnimation();
    },

    prepareAnimation: function(axis) {
        if (!this.isAxisEnabled(axis)) {
            return this;
        }

        var currentPosition = this.position[axis],
            flickStartPosition = this.flickStartPosition[axis],
            flickStartTime = this.flickStartTime[axis],
            maxPosition = this.getMaxPosition()[axis],
            maxAbsVelocity = this.getMaxAbsoluteVelocity(),
            boundValue = null,
            easing, velocity, duration;

        if (currentPosition < 0) {
            boundValue = 0;
        }
        else if (currentPosition > maxPosition) {
            boundValue = maxPosition;
        }

        
        if (boundValue !== null) {
            easing = this.getSnapEasing()[axis];
            easing.setConfig({
                startTime: this.dragEndTime,
                startValue: currentPosition,
                endValue: boundValue
            });
        }
        
        else {
            duration = this.dragEndTime - flickStartTime;

            if (duration === 0) {
                return false;
            }

            velocity = (currentPosition - flickStartPosition) / (this.dragEndTime - flickStartTime);

            if (velocity === 0) {
                return;
            }

            if (velocity < -maxAbsVelocity) {
                velocity = -maxAbsVelocity;
            }
            else if (velocity > maxAbsVelocity) {
                velocity = maxAbsVelocity;
            }

            easing = this.getMomentumEasing()[axis];
            easing.setConfig({
                startTime: this.dragEndTime,
                startValue: currentPosition,
                startVelocity: velocity,
                minMomentumValue: 0,
                maxMomentumValue: maxPosition
            });
        }

        this.activeEasing[axis] = easing;

        return this;
    },

    prepareSnapAnimation: function(axis) {
        if (!this.isAxisEnabled(axis)) {
            return false;
        }

        var currentPosition = this.position[axis],
            containerSize = this.getContainerSize()[axis],
            containerScrollSize = this.getContainerScrollSize()[axis],
            snap = this.getSnap(),
            offset = this.getSnapOffset()[axis],
            easing, endValue;

        endValue = Math.round((currentPosition + offset) / snap) * snap;

        
        if ((containerScrollSize - containerSize) <= currentPosition) {
            return false;
        }

        easing = this.getSnapEasing()[axis];
        easing.setConfig({
            startTime : Ext.Date.now(),
            startValue: currentPosition,
            endValue  : endValue - offset
        });

        this.activeEasing[axis] = easing;

        return endValue;
    },

    startAnimation: function() {
        this.isAnimating = true;
        this.animationTimer = setInterval(this.doAnimationFrame, this.animationInterval);
        this.doAnimationFrame();
    },

    doAnimationFrame: function() {
        if (!this.isAnimating) {
            return;
        }

        var easing = this.activeEasing,
            easingX = easing.x,
            easingY = easing.y,
            isEasingXEnded = easingX === null || easingX.isEnded,
            isEasingYEnded = easingY === null || easingY.isEnded,
            x = null,
            y = null;

        if (isEasingXEnded && isEasingYEnded) {
            this.stopAnimation();
            return;
        }

        if (!isEasingXEnded) {
            x = easingX.getValue();
        }

        if (!isEasingYEnded) {
            y = easingY.getValue();
        }

        this.scrollTo(x, y);
    },

    stopAnimation: function(isOnTouchStart) {
        if (!this.isAnimating) {
            return;
        }

        var activeEasing = this.activeEasing;

        activeEasing.x = null;
        activeEasing.y = null;

        this.isAnimating = false;

        clearInterval(this.animationTimer);

        this.snapToBoundary();

        if (!isOnTouchStart) {
            if (this.onScrollEnd()) {
                this.fireEvent('scrollend', this, this.position);
            }
        }

        this.isScrolling = false;
    },

    scrollToAnimated: function(x, y) {
        var currentPosition = this.position,
            easingX, easingY;

        easingX = this.getSnapEasing().x;
        easingX.setConfig({
            startTime : Ext.Date.now(),
            startValue: currentPosition.x,
            endValue  : x
        });

        easingY = this.getSnapEasing().y;
        easingY.setConfig({
            startTime : Ext.Date.now(),
            startValue: currentPosition.y,
            endValue  : y
        });

        this.activeEasing.x = easingX;
        this.activeEasing.y = easingY;

        this.startAnimation();
    },

    onScrollEnd: function() {
        if (this.isSnapping) {
            this.isSnapping = false;
            return true;
        }

        
        var snap = this.getSnap(),
            snapX, snapY;

        if (!snap) {
            return true;
        }

        snapX = this.prepareSnapAnimation('x');
        snapY = this.prepareSnapAnimation('y');

        if ((snapX || snapY) && snap && snap > 0) {
            this.isSnapping = true;

            this.startAnimation();

            return false;
        }

        return true;
    },

    snapValueForAxis: function(value, axis) {
        var containerSize = this.getContainerSize()[axis],
            containerScrollSize = this.getContainerScrollSize()[axis],
            snap = this.getSnap(),
            offset = this.getSnapOffset()[axis],
            easing;

        value = Math.round((value + offset) / snap) * snap;

        return value;
    },

    snapToBoundary: function() {
        var position    = this.position,
            maxPosition = this.getMaxPosition(),
            maxX = maxPosition.x,
            maxY = maxPosition.y,
            x = position.x,
            y = position.y;

        if (x < 0) {
            x = 0;
        }
        else if (x > maxX) {
            x = maxX;
        }

        if (y < 0) {
            y = 0;
        }
        else if (y > maxY) {
            y = maxY;
        }

        this.scrollTo(x, y);
    },

    destroy: function() {
        var element = this.getElement(),
            sizeMonitors = this.sizeMonitors;

        if (sizeMonitors) {
            sizeMonitors.element.destroy();
            sizeMonitors.container.destroy();
        }

        if (element) {
            element.removeCls(this.getCls());
            this.getContainer().removeCls(this.getContainerCls());
        }

        this.callParent(arguments);
    }

}, function() {

    this.override({
        constructor: function(config) {
            var acceleration, friction, springTension, minVelocity;

            if (config.hasOwnProperty('acceleration')) {
                acceleration = config.acceleration;
                delete config.acceleration;
                Ext.Logger.deprecate("'acceleration' config is deprecated, set momentumEasing.momentum.acceleration and momentumEasing.bounce.acceleration configs instead");

                Ext.merge(config, {
                    momentumEasing: {
                        momentum: { acceleration: acceleration },
                        bounce: { acceleration: acceleration }
                    }
                });
            }

            if (config.hasOwnProperty('friction')) {
                friction = config.friction;
                delete config.friction;
                Ext.Logger.deprecate("'friction' config is deprecated, set momentumEasing.momentum.friction config instead");

                Ext.merge(config, {
                    momentumEasing: {
                        momentum: { friction: friction }
                    }
                });
            }

            if (config.hasOwnProperty('springTension')) {
                springTension = config.springTension;
                delete config.springTension;
                Ext.Logger.deprecate("'springTension' config is deprecated, set momentumEasing.momentum.springTension config instead");

                Ext.merge(config, {
                    momentumEasing: {
                        momentum: { springTension: springTension }
                    }
                });
            }

            if (config.hasOwnProperty('minVelocityForAnimation')) {
                minVelocity = config.minVelocityForAnimation;
                delete config.minVelocityForAnimation;
                Ext.Logger.deprecate("'minVelocityForAnimation' config is deprecated, set momentumEasing.minVelocity config instead");

                Ext.merge(config, {
                    momentumEasing: {
                        minVelocity: minVelocity
                    }
                });
            }

            this.callOverridden(arguments);
        },

        updateBoundary: function() {
            Ext.Logger.deprecate("updateBoundary() is deprecated, use refresh() instead");
            return this.refresh();
        },

        scrollBy: function(offset) {
            var position = this.position;

            return this.scrollTo(position.x + offset.x, position.y + offset.y);
        },

        setOffset: function(offset) {
            return this.scrollTo(-offset.x, -offset.y);
        },

        
        snapToSlot: function() {

        }
    });

});


Ext.define('Ext.scroll.scroller.CssPosition', {
    extend: 'Ext.scroll.scroller.Abstract',

    doScrollTo: function(x, y) {
        var domStyle = this.getElement().dom.style;

        if (x !== null) {
            domStyle.left = (-x) + 'px';
        }

        if (y !== null) {
            domStyle.top = (-y) + 'px';
        }
    }
});


Ext.define('Ext.scroll.scroller.CssTransform', {
    extend: 'Ext.scroll.scroller.Abstract',

    doScrollTo: function(x, y) {
        var dom = this.getElement().dom,
            position = this.position;

        if (x === null) {
            x = position.x;
        }

        x = -x;

        if (y === null) {
            y = position.y;
        }

        y = -y;

        dom.style.webkitTransform = 'translate3d(' + x + 'px, ' + y + 'px, 0px)';
    }
});


Ext.define('Ext.scroll.scroller.Infinite', {
    extend: 'Ext.scroll.scroller.CssPosition',

    config: {
        itemLength: 30,

        slicesCount: 6,

        sliceLengthFactor: 1,

        functions: {
            render: Ext.emptyFn,

            recycle: Ext.emptyFn,

            activate: Ext.emptyFn,

            deactivate: Ext.emptyFn,

            scope: null
        },

        direction: 'vertical'
    },

    itemsCountPerSlice: 0,

    sliceLength: 0,

    recycleIndexOffset: 0,

    constructor: function() {
        this.preparedSlices = {};

        this.emptySlices = [];

        this.slices = [];

        this.activeSlices = {
            upper: null,
            lower: null
        };

        return this.callParent(arguments);
    },

    getMaxPosition: function(determine) {
        var maxPosition = this.maxPosition;

        if (determine) {
            maxPosition.x = Infinity;
            maxPosition.y = Infinity;
        }

        return maxPosition;
    },

    getCurrentAxis: function() {
        return (this.getDirection() === 'horizontal') ? 'x' : 'y';
    },

    applyDirection: function(direction) {
        if (direction !== 'vertical' && direction !== 'horizontal') {
            direction = 'vertical';
        }

        return direction;
    },

    applyItemLength: function(length) {
        if (typeof length == 'number' && length > 0) {
            return length;
        }
        Ext.Logger.error("Invalid itemLength, must be a number greater than 0");
    },

    updateItemLength: function(length, oldLength) {
        var containerSize = this.getContainerSize(true),
            sliceLengthFactor = this.getSliceLengthFactor(),
            itemsCountPerSlice,
            width, height, sliceLength;

        if (this.isAxisEnabled('x')) {
            height = containerSize.y;
            itemsCountPerSlice = Math.ceil(containerSize.x / length) * sliceLengthFactor;
            sliceLength = width = itemsCountPerSlice * length;
        }
        else {
            width = containerSize.x;
            itemsCountPerSlice = Math.ceil(containerSize.y / length) * sliceLengthFactor;
            sliceLength = height = itemsCountPerSlice * length;
        }

        this.itemsCountPerSlice = itemsCountPerSlice;
        this.sliceLength = sliceLength;

        this.setSliceSize(width, height);

        if (oldLength) {
            this.refresh();
        }
    },

    applySlicesCount: function(count) {
        if (typeof count == 'number' && count >= 4) {
            return count;
        }
        Ext.Logger.error("Invalid slicesCount, must be a number greater or equal to 4");
    },

    updateSlicesCount: function(count, oldCount) {
        var slices = this.slices,
            emptySlices = this.emptySlices,
            slice, i;

        if (oldCount) {
            this.destroySlices();
        }

        for (i = 0; i < count; i++) {
            slice = this.createSlice();
            slices[i] = slice;
            emptySlices.push(slice);
        }

        this.recycleIndexOffset = Math.floor((count - 2) / 2);

        if (oldCount) {
            this.refresh();
        }
    },

    destroySlices: function() {
        var slices = this.slices,
            i, ln, slice;

        for (i = 0, ln = slices.length; i < ln; i++) {
            slice.destroy();
        }

        slices.length = 0;
        this.emptySlices.length = 0;
        this.preparedSlices.length = 0;
    },

    createSlice: function() {
        var element = this.getElement(),
            slice = element.createChild({}),
            style = slice.dom.style;

        style.position = 'absolute';


        return slice;
    },

    setSliceSize: function(width, height) {
        this.getSlicesCount();

        var slices = this.slices,
            i, ln, slice, style;

        width = width + 'px';
        height = height + 'px';

        for (i = 0,ln = slices.length; i < ln; i++) {
            slice = slices[i];

            style = slice.dom.style;
            style.width = width;
            style.height = height;
        }

        return this;
    },

    prepareSlice: function(index) {
        var preparedSlices = this.preparedSlices,
            itemsCountPerSlice = this.itemsCountPerSlice,
            functions = this.getFunctions(),
            startItemIndex, endItemIndex, slice;

        if (!preparedSlices[index]) {
            slice = this.getEmptySlice();
            startItemIndex = index * itemsCountPerSlice;
            endItemIndex = startItemIndex + itemsCountPerSlice - 1;

            preparedSlices[index] = slice;

            functions.render.call(functions.scope, slice, startItemIndex, endItemIndex);
        }

        return preparedSlices[index];
    },

    getSlice: function(index) {
        if (index > 0) {
            this.prepareSlice(index - 1);
        }

        this.prepareSlice(index + 1);

        return this.prepareSlice(index);
    },

    getEmptySlice: function() {
        var recycleIndexOffset = this.recycleIndexOffset,
            upperIndex = this.upperSliceIndex - recycleIndexOffset,
            lowerIndex = this.lowerSliceIndex + recycleIndexOffset,
            preparedSlices = this.preparedSlices,
            emptySlices = this.emptySlices,
            i;

        for (i in preparedSlices) {
            if (preparedSlices.hasOwnProperty(i)) {
                if (i <= upperIndex || i >= lowerIndex) {
                    emptySlices.push(preparedSlices[i]);
                    delete preparedSlices[i];
                }
            }
        }

        return emptySlices.pop();
    },

    setSlicePosition: function(slice, position, axis) {
        var style = slice.dom.style;

        position = (-position) + 'px';

        if (axis === 'x') {
            style.left = position;
        }
        else {
            if (Ext.os.is.iOS || Ext.os.is.Android3) {
                style.webkitTransform = 'translate3d(0px, ' + position + ', 0px)';
            }
            else {
                style.top = position;
            }
        }
    },

    setActiveSlices: function(upper, lower) {
        var activeSlices = this.activeSlices,
            oldUpper = activeSlices.upper,
            oldLower = activeSlices.lower;

        if (oldUpper && oldLower) {
            if (oldUpper !== upper) {
                if (oldUpper !== lower) {
                    this.deactivateSlice(oldUpper);
                }

                if (upper !== oldLower) {
                    this.activateSlice(upper, 2);
                }
            }

            if (oldLower !== lower) {
                if (oldLower !== upper) {
                    this.deactivateSlice(oldLower);
                }

                if (lower !== oldUpper) {
                    this.activateSlice(lower, 1);
                }
            }
        }
        else {
            this.activateSlice(upper, 2);
            this.activateSlice(lower, 1);
        }

        activeSlices.upper = upper;
        activeSlices.lower = lower;

        return this;
    },

    activateSlice: function(slice, zIndex) {
        var functions = this.getFunctions(),
            style = slice.dom.style;




        functions.activate.call(functions.scope, slice);
    },

    deactivateSlice: function(slice) {
        var functions = this.getFunctions(),
            style = slice.dom.style;



        if (Ext.os.is.iOS || Ext.os.is.Android3) {
            style.webkitTransform = 'translate3d(0px, -10000px, 0px)';
        }
        else {
            style.top = '-10000px';
        }



        functions.deactivate.call(functions.scope, slice);
    },

    doScrollTo: function(x, y) {
        var axis = this.getCurrentAxis(),
            sliceLength = this.sliceLength,
            upperPosition = ((axis === 'x') ? x : y),
            upperSliceIndex = Math.max(0, Math.floor(upperPosition / this.sliceLength)),
            lowerSliceIndex = upperSliceIndex + 1;

        this.upperSliceIndex = upperSliceIndex;
        this.lowerSliceIndex = lowerSliceIndex;

        var upperSlice = this.getSlice(upperSliceIndex),
            lowerSlice = this.getSlice(lowerSliceIndex),
            containerSize = this.getContainerSize()[axis],
            lowerPosition;

        upperPosition = upperPosition % sliceLength;
        lowerPosition = upperPosition - sliceLength;

        this.setActiveSlices(upperSlice, lowerSlice);

        this.setSlicePosition(upperSlice, upperPosition, axis);

        if (lowerPosition >= -containerSize) {
            this.setSlicePosition(lowerSlice, lowerPosition, axis);
        }
    }
});


Ext.define('Ext.scroll.scroller.ScrollPosition', {
    extend: 'Ext.scroll.scroller.CssPosition',

    config: {
        stretcherCls: 'x-scroll-stretcher'
    },

    constructor: function() {
        this.stretchSize = { x: 0, y: 0 };

        return this.callParent(arguments);
    },

    getStretcher: function() {
        var stretcher = this.stretcher,
            element;

        if (!stretcher) {
            element = this.getElement();

            if (element) {
                stretcher = this.stretcher = Ext.Element.create({
                    className: this.getStretcherCls()
                });
                stretcher.insertBefore(element);
            }
        }

        return stretcher;
    },

    stretch: function(x, y) {
        var strechSize = this.stretchSize,
            stretcher = this.getStretcher(),
            element = this.getElement();

        strechSize.x = x;
        strechSize.y = y;

        stretcher.setWidth(x * 3);
        stretcher.setHeight(y * 3);

        element.setLeft(x);
        element.setTop(y);

        return this;
    },

    shrink: function() {
        var stretcher = this.getStretcher(),
            element = this.getElement();

        stretcher.setWidth(0);
        stretcher.setHeight(0);

        element.setLeft(0);
        element.setTop(0);
    },

    doScrollTo: function(x, y) {
        var containerDom = this.getContainer().dom,
            stretchSize = this.stretchSize;

        if (x !== null) {
            containerDom.scrollLeft = x + stretchSize.x;
        }

        if (y !== null) {
            containerDom.scrollTop = y + stretchSize.y;
        }
    },

    determinePosition: function() {
        var containerDom = this.getContainer().dom,
            stretchSize = this.stretchSize;

        return {
            x: containerDom.scrollLeft - stretchSize.x,
            y: containerDom.scrollTop - stretchSize.y
        };
    },

    onTouchStart: function() {
        var position = this.determinePosition();

        this.scrollTo(position.x, position.y);

        this.callParent(arguments);
    },

    onAfterInitialized: function() {
        this.callParent(arguments);

        this.refreshStretch();
    },

    refresh: function() {
        this.callParent(arguments);

        this.refreshStretch();
    },

    refreshStretch: function() {
        var position = this.position,
            size,
            containerSize,
            stretchX, stretchY;

        this.shrink();

        size = this.getSize();
        containerSize = this.getContainerSize();

        stretchX = Math.max(size.x, containerSize.x);
        stretchY = Math.max(size.y, containerSize.y);

        this.stretch(stretchX, stretchY);

        this.doScrollTo(position.x, position.y);
    },

    destroy: function() {
        var element = this.getElement();

        if (element) {
            this.getStretcher().destroy();
            element.setLeft(null);
            element.setTop(null);
        }

        this.callParent(arguments);
    }
});


Ext.define('Ext.scroll.Scroller', {
    alternateClassName: 'Ext.util.Scroller',

    requires: [
        'Ext.scroll.scroller.ScrollPosition',
        'Ext.scroll.scroller.CssPosition',
        'Ext.scroll.scroller.CssTransform'
    ],

    defaultConfig: {
        fps: 'auto',

        scrollMethod: 'auto'
    },
    
    

    
    
    constructor: function(config) {
        var namespace = Ext.scroll.scroller,
            ScrollPosition = namespace.ScrollPosition,
            CssTransform = namespace.CssTransform,
            CssPosition = namespace.CssPosition,
            Scroller = ScrollPosition,
            osName = Ext.os.name,
            osVersion = Ext.os.version,
            userAgent = Ext.browser.userAgent,
            scrollMethod, fps, element;

        if (arguments.length == 2) {
            Ext.Logger.deprecate("Passing element as the first argument is deprecated, pass it as the 'element' property of the config object instead");
            element = config;
            config = arguments[1];

            if (!config) {
                config = {};
            }

            config.element = element;
        }

        if (typeof config == 'string') {
            config = {
                direction: config
            };
        }

        config = Ext.merge({}, this.defaultConfig, config || {});

        if (config.fps === 'auto') {
            if (/(htc|desire|incredible|adr6300)/i.test(userAgent) && osVersion.lt('2.3')) {
                fps = 30;
            }
            else if (Ext.os.is.Android && !/(droid2)/i.test(userAgent)) {
                fps = 50;
            }
            else {
                fps = 60;
            }

            config.fps = fps;
        }

        scrollMethod = config.scrollMethod.toLowerCase();

        switch (scrollMethod) {
            case 'csstransform':
                Scroller = CssTransform;
                break;

            case 'cssposition':
                Scroller = CssPosition;
                break;

            case 'scrollposition':
                Scroller = ScrollPosition;
                break;

            case 'auto':
                if (/^(iOS|RIMTablet|MacOS|Windows)$/.test(osName) || Ext.os.is.BlackBerry) {
                    Scroller = CssTransform;
                }
                break;

            default:
                Ext.Logger.error("Unrecognized scrollMethod config value of '" +
                        scrollMethod + "'. Valid values are: 'csstransform', " +
                        "'cssposition', 'scrollposition' or 'auto'.");

        }

        return new Scroller(config);
    }

});


Ext.define('Ext.util.TapRepeater', {
    requires: ['Ext.DateExtras'],

    mixins: {
        observable: 'Ext.mixin.Observable'
    },

    
    
    

    config: {
        el: null,
        accelerate: true,
        interval: 10,
        delay: 250,
        preventDefault: true,
        stopDefault: false,
        timer: 0,
        pressCls: null
    },

    
    constructor: function(config) {
        var me = this;
        for (var configName in config) {
            if (me.self.prototype.config && !(configName in me.self.prototype.config)) {
                me[configName] = config[configName];
                Ext.Logger.warn('Applied config as instance property: "' + configName + '"', me);
            }
        }
        me.initConfig(config);
    },

    updateEl: function(newEl, oldEl) {
        var eventCfg = {
                touchstart: 'onTouchStart',
                touchend: 'onTouchEnd',
                tap: 'eventOptions',
                scope: this
            };
        if (oldEl) {
            oldEl.un(eventCfg)
        }
        newEl.on(eventCfg);
    },

    
    eventOptions: function(e) {
        if (this.getPreventDefault()) {
            e.preventDefault();
        }
        if (this.getStopDefault()) {
            e.stopEvent();
        }
    },

    
    destroy: function() {
        this.clearListeners();
        Ext.destroy(this.el);
    },

    
    onTouchStart: function(e) {
        var me = this,
            pressCls = me.getPressCls();
        clearTimeout(me.getTimer());
        if (pressCls) {
            me.getEl().addCls(pressCls);
        }
        me.tapStartTime = new Date();

        me.fireAction('touchstart', [me, e], 'doTouchStart');
        me.fireAction('tap', [me, e], 'doTap');

        
        if (me.getAccelerate()) {
            me.delay = 400;
        }
        me.setTimer(Ext.defer(me.tap, me.getDelay() || me.getInterval(), me, [e]));
    },

    doTouchStart: Ext.emptyFn,
    doTouchEnd: Ext.emptyFn,
    doTap: Ext.emptyFn,

    
    tap: function(e) {
        var me = this;
        me.fireAction('tap', [me, e], 'doTap');
        me.setTimer(Ext.defer(me.tap, me.getAccelerate() ? me.easeOutExpo(Ext.Date.getElapsed(me.tapStartTime),
            400,
            -390,
            12000) : me.getInterval(), me, [e]));
    },

    
    
    easeOutExpo: function(t, b, c, d) {
        return (t == d) ? b + c : c * ( - Math.pow(2, -10 * t / d) + 1) + b;
    },

    
    onTouchEnd: function(e) {
        var me = this;
        clearTimeout(me.getTimer());
        me.getEl().removeCls(me.getPressCls());
        me.fireAction('touchend', [me, e], 'doTouchEnd');
    }
});


Ext.define('Ext.AbstractComponent', {
    extend: 'Ext.EventedBase',

    onClassExtended: function(Class, members) {
        if (!members.hasOwnProperty('cachedConfig')) {
            return;
        }

        var prototype = Class.prototype,
            config = members.config,
            cachedConfig = members.cachedConfig,
            cachedConfigList = prototype.cachedConfigList,
            hasCachedConfig = prototype.hasCachedConfig,
            name, value;

        delete members.cachedConfig;

        prototype.cachedConfigList = cachedConfigList = (cachedConfigList) ? cachedConfigList.slice() : [],
        prototype.hasCachedConfig = hasCachedConfig = (hasCachedConfig) ? Ext.Object.chain(hasCachedConfig) : {};

        if (!config) {
            members.config = config = {};
        }

        for (name in cachedConfig) {
            if (cachedConfig.hasOwnProperty(name)) {
                value = cachedConfig[name];

                if (!hasCachedConfig[name]) {
                    hasCachedConfig[name] = true;
                    cachedConfigList.push(name);
                }

                config[name] = value;
            }
        }
    },

    constructor: function(config) {
        var prototype = this.self.prototype,
            configNameCache, defaultConfig, cachedConfigList, initConfigList, hasInitConfigMap,
            referenceList, reference, renderTemplate, renderElement, elements,
            i, ln, element, name, nameMap, initializedName, internalName;

        this.initElement();

        
        if (!prototype.hasOwnProperty('renderTemplate')) {
            referenceList = this.referenceList;
            configNameCache = Ext.Class.configNameCache;
            defaultConfig = this.config;
            cachedConfigList = this.cachedConfigList;
            initConfigList = this.initConfigList;
            hasInitConfigMap = this.hasInitConfigMap;

            for (i = 0,ln = cachedConfigList.length; i < ln; i++) {
                name = cachedConfigList[i];
                nameMap = configNameCache[name];
                initializedName = nameMap.initialized;

                prototype[initializedName] = true;

                if (hasInitConfigMap[name]) {
                    delete hasInitConfigMap[name];
                    Ext.Array.remove(initConfigList, name);
                }

                if (defaultConfig[name] !== null) {
                    internalName = nameMap.internal;
                    this[internalName] = null;
                    this[nameMap.set](defaultConfig[name]);
                    prototype[internalName] = this[internalName];
                }
            }

            renderElement = this.renderElement.dom;
            prototype.renderTemplate = renderTemplate = document.createDocumentFragment();
            renderTemplate.appendChild(renderElement.cloneNode(true));

            elements = renderTemplate.querySelectorAll('[id]');

            for (i = 0,ln = elements.length; i < ln; i++) {
                element = elements[i];
                element.removeAttribute('id');
            }

            for (i = 0,ln = referenceList.length; i < ln; i++) {
                reference = referenceList[i];
                this[reference].dom.removeAttribute('reference');
            }
        }

        this.callParent(arguments);
    },

    getElementConfig: Ext.emptyFn,

    referenceAttributeName: 'reference',

    referenceSelector: '[reference]',

    
    addReferenceNode: function(name, domNode) {
        this.__defineGetter__(name, function() {
            var reference;

            delete this[name];
            this[name] = reference = new Ext.Element(domNode);
            return reference;
        });
    },

    initElement: function() {
        var id = this.getId(),
            referenceList = [],
            cleanAttributes = true,
            referenceAttributeName = this.referenceAttributeName,
            renderTemplate, renderElement, element,
            referenceNodes, i, ln, referenceNode, reference;

        if (this.self.prototype.hasOwnProperty('renderTemplate')) {
            renderTemplate = this.renderTemplate.cloneNode(true);
            renderElement = renderTemplate.firstChild;
        }
        else {
            cleanAttributes = false,
            renderTemplate = document.createDocumentFragment();
            renderElement = Ext.Element.create(this.getElementConfig(), true);
            renderTemplate.appendChild(renderElement);
        }

        referenceNodes = renderTemplate.querySelectorAll(this.referenceSelector);

        for (i = 0,ln = referenceNodes.length; i < ln; i++) {
            referenceNode = referenceNodes[i];
            reference = referenceNode.getAttribute(referenceAttributeName);

            if (cleanAttributes) {
                referenceNode.removeAttribute(referenceAttributeName);
            }

            if (reference == 'element') {
                referenceNode.id = id;
                this.element = element = new Ext.Element(referenceNode);
            }
            else {
                this.addReferenceNode(reference, referenceNode);
            }

            referenceList.push(reference);
        }

        this.referenceList = referenceList;

        if (!this.innerElement) {
            this.innerElement = element;
        }

        if (renderElement === element.dom) {
            this.renderElement = element;
        }
        else {
            this.addReferenceNode('renderElement', renderElement);
        }

        return this;
    }
});

(function(clsPrefix) {


Ext.define('Ext.Component', {

    extend: 'Ext.AbstractComponent',

    alternateClassName: 'Ext.lib.Component',

    mixins: ['Ext.mixin.Traversable'],

    requires: [
        'Ext.ComponentManager',
        'Ext.XTemplate',
        'Ext.dom.Element',
        'Ext.behavior.Draggable'
    ],

    xtype: 'component',

    observableType: 'component',

    cachedConfig: {
        

        
        baseCls: null,

        
        cls: null,

        
        ui: null,

        
        margin: null,

        
        padding: null,

        
        border: null,

        
        styleHtmlCls: clsPrefix + 'html',

        
        styleHtmlContent: null,

        
        masked: null,

        
        hidden: false
    },

    eventedConfig: {
        
        left: null,

        
        top: null,

        
        right: null,

        
        bottom: null,

        
        width: null,

        
        height: null,

        
        docked: null,

        
        centered: null,

        
        hidden: null,

        
        disabled: null
    },

    config: {
        
        style: null,

        
        html: null,

        
        draggable: null,

        
        droppable: null,

        
        renderTo: null,

        
        zIndex: null,

        
        tpl: null,

        

        
        tplWriteMode: 'overwrite',

        
        data: null,

        
        disabledCls: clsPrefix + 'item-disabled',

        
        modal: null,

        
        hideOnMaskTap: true,

        
        contentEl: null,

        
        itemId: undefined
    },

    

    

    

    

    
    alignPositionMap: [
        'tl-bl',
        't-b',
        'tr-br',
        'l-r',
        'l-r',
        'r-l',
        'bl-tl',
        'b-t',
        'br-tr'
    ],

    
    isComponent: true,

    
    floating: false,

    
    rendered: false,

    
    maskText: null,

    
    maskTextCls: clsPrefix + 'mask-msg',

    
    loadingMask: false,

    
    dockPositions: {
        top: true,
        right: true,
        bottom: true,
        left: true
    },

    innerElement: null,

    element: null,

    template: [],

    
    constructor: function(config) {
        if (config && config.id) {
            this.id = config.id;
            delete config.id;
        }
        else {
            this.getId();
        }

        Ext.ComponentManager.register(this);

        this.callParent(arguments);

        
        if ('fullscreen' in this.config) {
            this.fireEvent('fullscreen', this);
        }
    },

    getTemplate: function() {
        return this.template;
    },

    getElementConfig: function() {
        return {
            reference: 'element',
            children: this.getTemplate()
        };
    },

    
    getEl: function() {
        return this.renderElement;
    },

    renderTo: function(container, insertBeforeElement) {
        var dom = this.renderElement.dom,
            containerDom = Ext.getDom(container),
            insertBeforeChildDom = Ext.getDom(insertBeforeElement);

        if (containerDom) {
            if (insertBeforeChildDom) {
                containerDom.insertBefore(dom, insertBeforeChildDom);
            }
            else {
                containerDom.appendChild(dom);
            }

            this.setRendered(Boolean(dom.offsetParent));
        }
    },

    setParent: function(parent) {
        var currentParent = this.parent;

        if (parent && currentParent && currentParent !== parent) {
            currentParent.remove(this, false);
        }

        this.parent = parent;

        return this;
    },

    updateRenderTo: function(newContainer) {
        this.renderTo(newContainer);
    },

    updateStyle: function(style) {
        this.element.dom.setAttribute('style', style);
    },

    updateBorder: function(border) {
        this.element.setBorder(border);
    },

    updatePadding: function(padding) {
       this.innerElement.setPadding(padding);
    },

    updateMargin: function(margin) {
        this.element.setMargin(margin);
    },

    updateUi: function(newUi, oldUi) {
        var baseCls = this.getBaseCls();

        if (baseCls) {
            if (newUi) {
                this.addCls(newUi, baseCls);
            }

            if (oldUi) {
                this.removeCls(oldUi, baseCls);
            }
        }
    },

    applyBaseCls: function(baseCls) {
        return baseCls || clsPrefix + this.xtype;
    },

    updateBaseCls: function(newBaseCls, oldBaseCls) {
        var me = this,
            ui = me.getUi();

        if (newBaseCls) {
            this.addCls(newBaseCls);

            if (ui) {
                this.addCls(newBaseCls, null, ui);
            }
        }

        if (oldBaseCls) {
            this.removeCls(oldBaseCls);

            if (ui) {
                this.removeCls(oldBaseCls, null, ui);
            }
        }
    },

    updateCls: function(cls, oldCls) {
        this.replaceCls(oldCls, cls);
    },

    
    updateStyleHtmlCls: function(newHtmlCls, oldHtmlCls) {
        var innerHtmlElement = this.innerHtmlElement,
            innerElement = this.innerElement;

        if (this.getStyleHtmlContent() && oldHtmlCls) {
            if (innerHtmlElement) {
                innerHtmlElement.replaceCls(oldHtmlCls, newHtmlCls);
            } else {
                innerElement.replaceCls(oldHtmlCls, newHtmlCls);
            }
        }
    },

    updateStyleHtmlContent: function(styleHtmlContent) {
        var htmlCls = this.getStyleHtmlCls(),
            innerElement = this.innerElement,
            innerHtmlElement = this.innerHtmlElement;

        if (styleHtmlContent) {
            if (innerHtmlElement) {
                innerHtmlElement.addCls(htmlCls);
            } else {
                innerElement.addCls(htmlCls);
            }
        } else {
            if (innerHtmlElement) {
                innerHtmlElement.removeCls(htmlCls);
            } else {
                innerElement.addCls(htmlCls);
            }
        }
    },

    applyContentEl: function(contentEl) {
        if (contentEl) {
            return Ext.get(contentEl);
        }
    },

    updateContentEl: function(newContentEl, oldContentEl) {
        if (oldContentEl) {
            oldContentEl.hide();
            Ext.getBody().append(oldContentEl);
        }
        if (newContentEl) {
            this.setHtml(newContentEl);
            newContentEl.show();
        }
    },

    
    getSize: function() {
        return {
            width: this.getWidth(),
            height: this.getHeight()
        };
    },

    isCentered: function() {
        return Boolean(this.getCentered());
    },

    isFloating: function() {
        return this.floating;
    },

    isDocked: function() {
        return Boolean(this.getDocked());
    },

    isInnerItem: function() {
        var me = this;
        return !me.isCentered() && !me.isFloating() && !me.isDocked();
    },

    filterPositionValue: function(value) {
        if (value === '' || value === 'auto') {
            value = null;
        }

        return value;
    },

    applyTop: function(top) {
        return this.filterPositionValue(top);
    },

    applyRight: function(right) {
        return this.filterPositionValue(right);
    },

    applyBottom: function(bottom) {
        return this.filterPositionValue(bottom);
    },

    applyLeft: function(left) {
        return this.filterPositionValue(left);
    },

    doSetTop: function(top) {
        this.updateFloating();
        this.element.setTop(top);
    },

    doSetRight: function(right) {
        this.updateFloating();
        this.element.setRight(right);
    },

    doSetBottom: function(bottom) {
        this.updateFloating();
        this.element.setBottom(bottom);
    },

    doSetLeft: function(left) {
        this.updateFloating();
        this.element.setLeft(left);
    },

    doSetWidth: function(width) {
        this.element.setWidth(width);
    },

    doSetHeight: function(height) {
        this.element.setHeight(height);
    },

    































    applyCentered: function(centered) {
        centered = Boolean(centered);

        if (centered) {
            if (this.isFloating()) {
                this.resetFloating();
            }

            if (this.isDocked()) {
                this.setDocked(false);
            }
        }

        return centered;
    },

    doSetCentered: Ext.emptyFn,

    applyDocked: function(docked) {
        if (docked) {
            if (!this.dockPositions[docked]) {
                Ext.Logger.error("Invalid docking position of '" + docked + "', must be either 'top', 'right', 'bottom', " +
                    "'left' or `null` (for no docking)", this);
                return;
            }

            if (this.isFloating()) {
                this.resetFloating();
            }

            if (this.isCentered()) {
                this.setCentered(false);
            }
        }

        return docked;
    },

    doSetDocked: Ext.emptyFn,

    resetFloating: function() {
        this.setTop(null);
        this.setRight(null);
        this.setBottom(null);
        this.setLeft(null);
    },

    updateFloating: function() {
        var floating = true;

        if (this.getTop() === null && this.getBottom() === null && this.getRight() === null && this.getLeft() === null) {
            floating = false;
        }

        if (floating !== this.floating) {
            if (floating) {
                if (this.isCentered()) {
                    this.setCentered(false);
                }

                if (this.isDocked()) {
                    this.setDocked(false);
                }
            }

            this.floating = floating;
            this.fireEvent('floatingchange', this, floating);
        }
    },

    applyDisabled: function(disabled) {
        return Boolean(disabled);
    },

    doSetDisabled: function(disabled) {
        this.element[disabled ? 'addCls' : 'removeCls'](this.getDisabledCls());
    },

    
    disable: function() {
       this.setDisabled(true);
    },

    
    enable: function() {
        this.setDisabled(false);
    },

    
    isDisabled: function() {
        return this.getDisabled();
    },

    updateZIndex: function(zIndex) {
        this.element.dom.style.zIndex = zIndex;
    },

    getInnerHtmlElement: function() {
        var innerHtmlElement = this.innerHtmlElement,
            styleHtmlCls = this.getStyleHtmlCls();

        if (!innerHtmlElement || !innerHtmlElement.dom || !innerHtmlElement.dom.parentNode) {
            this.innerHtmlElement = innerHtmlElement = this.innerElement.createChild({ cls: 'x-innerhtml ' });

            if (this.getStyleHtmlContent()) {
                this.innerHtmlElement.addCls(styleHtmlCls);
                this.innerElement.removeCls(styleHtmlCls);
            }
        }

        return innerHtmlElement;
    },

    updateHtml: function(html) {
        var innerHtmlElement = this.getInnerHtmlElement();
        if (typeof html === 'string') {
            innerHtmlElement.setHtml(html);
        } else {
            innerHtmlElement.setHtml('');
            innerHtmlElement.append(html);
        }
    },

    applyHidden: function(hidden) {
        return Boolean(hidden);
    },

    doSetHidden: function(hidden) {
        var element = this.renderElement;

        if (hidden) {
            element.hide();
        }
        else {
            element.show();
        }
    },

    
    isHidden: function() {
        return this.getHidden();
    },

    
    hide: function() {
        this.setHidden(true);
    },

    
    show: function() {
        this.setHidden(false);
    },

    
    isRendered: function() {
        return this.rendered;
    },

    
    isPainted: function() {
        return this.renderElement.isPainted();
    },

    
    applyTpl: function(config) {
        return (Ext.isObject(config) && config.isTemplate) ? config : new Ext.XTemplate(config);
    },

    
    updateData: function(newData) {
        var me = this;
        if (newData) {
            var tpl = me.getTpl(),
                tplWriteMode = me.getTplWriteMode();

            if (tpl) {
                tpl[tplWriteMode](me.element, newData);
            }
        }
    },

    
    addCls: function(cls, prefix, suffix) {
        this.element.addCls(cls, prefix, suffix);
    },

    
    removeCls: function(cls, prefix, suffix) {
        this.element.removeCls(cls, prefix, suffix);
    },

    replaceCls: function(oldCls, newCls, prefix, suffix) {
        this.element.replaceCls(oldCls, newCls, prefix, suffix);
    },

    applyItemId: function(itemId) {
        return itemId || this.getId();
    },

    
    
    isXType: function(xtype, shallow) {
        if (shallow) {
            return this.xtypes.indexOf(xtype) != -1;
        }

        return Boolean(this.xtypesMap[xtype]);
    },

    
    
    
    getXTypes: function() {
        return this.xtypesChain.join('/');
    },

    getDraggableBehavior: function() {
        var behavior = this.draggableBehavior;

        if (!behavior) {
            behavior = this.draggableBehavior = new Ext.behavior.Draggable(this);
        }

        return behavior;
    },

    applyDraggable: function(config) {
        this.getDraggableBehavior().setConfig(config);
    },

    getDraggable: function() {
        return this.getDraggableBehavior().getDraggable();
    },

    setRendered: function(rendered) {
        var wasRendered = this.rendered;

        if (rendered !== wasRendered) {
            this.rendered = rendered;

            return true;
        }

        return false;
    },

    
    setSize: function(width, height) {
        if (width != undefined) {
            this.setWidth(width);
        }
        if (height != undefined) {
            this.setHeight(height);
        }
    },

    
    
    showBy: function(alignTo, animation, anchor) {
        
        if (animation) {
            Ext.Logger.warn('showBy: animation argument not implemented.');
        }

        var parent = this.getParent();
        if (parent) {
            parent.remove(this, false);
        }

        Ext.Viewport.add(this);

        this.setTop(-10000);
        this.setLeft(-10000);
        this.show();

        this.alignTo(alignTo, anchor || 'auto');

        
        
        this.element.repaint();
    },

    anchorRe: /^([a-z]+)-([a-z]+)(\?)?$/,

    doAnchorXY: function(anchor, box, toBox, constrainBox) {
        var matches = anchor.match(this.anchorRe),
            fromAnchor = matches[1].split(''),
            toAnchor = matches[2].split(''),
            offsetBox = {top: toBox.top, left: toBox.left},
            constrain = (matches[3] === '?'),

            fromVertical = fromAnchor[0],
            fromHorizontal = fromAnchor[1] || fromVertical,

            toVertical = toAnchor[0],
            toHorizontal = toAnchor[1] || toVertical,

            maxLeft, maxTop;

        switch (fromVertical) {
            case 't':
                switch (toVertical) {
                    case 'c':
                        offsetBox.top += toBox.height / 2;
                        break;
                    case 'b':
                        offsetBox.top += toBox.height;
                }
                break;

            case 'b':
                switch (toVertical) {
                    case 'c':
                        offsetBox.top -= (box.height - (toBox.height / 2));
                        break;
                    case 't':
                        offsetBox.top -= box.height;
                }
                break;

            case 'c':
                 switch (toVertical) {
                    case 't':
                        offsetBox.top -= (box.height / 2);
                        break;
                    case 'c':
                        offsetBox.top -= ((box.height / 2) - (toBox.height / 2));
                        break;
                    case 'b':
                        offsetBox.top -= ((box.height / 2) - toBox.height);
                }
                break;
        }

        switch (fromHorizontal) {
            case 'l':
                switch (toHorizontal) {
                    case 'c':
                        offsetBox.left += toBox.width / 2;
                        break;
                    case 'r':
                        offsetBox.left += toBox.width;
                }
                break;

            case 'r':
                switch (toHorizontal) {
                    case 'r':
                        offsetBox.left -= (box.width - toBox.width);
                        break;
                    case 'c':
                        offsetBox.left -= (box.width - (toBox.width / 2));
                        break;
                    case 'l':
                        offsetBox.left -= box.width;
                }
                break;

            case 'c':
                 switch (toHorizontal) {
                    case 'l':
                        offsetBox.left -= (box.width / 2);
                        break;
                    case 'c':
                        offsetBox.left -= ((box.width / 2) - (toBox.width / 2));
                        break;
                    case 'r':
                        offsetBox.left -= ((box.width / 2) - toBox.width);
                }
                break;
        }

        if (constrain) {
            maxLeft = (constrainBox.left + constrainBox.width) - box.width;
            maxTop = (constrainBox.top + constrainBox.height) - box.height;

            offsetBox.left = Math.max(constrainBox.left, Math.min(maxLeft, offsetBox.left));
            offsetBox.top = Math.max(constrainBox.top, Math.min(maxTop, offsetBox.top));
        }

        return offsetBox;
    },

    alignTo : function(alignTo, anchor, offset) {
        offset = offset || 0;

        var alignElement = alignTo.element,
            alignXY = webkitConvertPointFromNodeToPage(alignElement.dom, new WebKitPoint()),
            alignSize = alignElement.getSize(),
            size = this.element.getSize(),
            parent = this.getParent(),
            constrainBox = (parent) ? parent.element.getBox() : Ext.getBody().getBox(),
            box = {
                left: 0,
                top: 0,
                width: size.width,
                height: size.height
            },
            relativeToBox = {
                left: alignXY.x,
                top: alignXY.y,
                width: alignSize.width,
                height: alignSize.height
            }, anchorBox, tmpBox;

        if (anchor == 'auto') {
            anchor = 'tc-bc';
        }

        anchorBox = this.doAnchorXY(anchor, box, relativeToBox, constrainBox);
        if (anchorBox.top + box.height > constrainBox.top + constrainBox.height) {
            tmpBox = this.doAnchorXY('bc-tc?', box, relativeToBox, constrainBox);
            anchorBox.top = tmpBox.top;
        }
        if (anchorBox.left + box.width > constrainBox.left + constrainBox.width) {
            tmpBox = this.doAnchorXY('br-tr?', box, relativeToBox, constrainBox);
            anchorBox.left = tmpBox.left;
        } else if (anchorBox.left < constrainBox.left) {
            tmpBox = this.doAnchorXY('bl-tl?', box, relativeToBox, constrainBox);
            anchorBox.left = tmpBox.left;
        }

        this.setTop(anchorBox.top);
        this.setLeft(anchorBox.left);
    },

    
    up: function(selector) {
        var result = this.parent;

        if (selector) {
            for (; result; result = result.parent) {
                if (Ext.ComponentQuery.is(result, selector)) {
                    return result;
                }
            }
        }
        return result;
    },

    updateMasked: function(newMasked) {
        var me          = this,
            element     = me.element,
            maskEl      = me.maskEl,
            loadingMask = me.loadingMask,
            maskText    = me.maskText,
            maskTextCls = me.maskTextCls,
            prefix      = Ext.baseCSSPrefix,
            cls         = [prefix + 'mask'],
            children    = [];

        if (newMasked) {
            if (!maskEl) {
                
                me.maskEl = element.createChild({
                    cls: cls.join(' '),
                    children: [
                        {
                            cls: 'x-mask-inner',
                            children: [
                                {
                                    cls: prefix + 'loading-spinner-outer',
                                    html: Ext.LoadingSpinner
                                },
                                {
                                    cls : prefix + 'mask-msg',
                                    html: maskText
                                }
                            ]
                        }
                    ]
                });

                
                if (loadingMask) {
                    me.maskEl.addCls(prefix + 'mask-loading');
                }

                
                if (maskText) {
                    me.maskEl.addCls(prefix + 'mask-text');
                }
            }

            
            me.addCls('masked', prefix);

            
            me.maskEl.show();
        } else if (maskEl) {
            me.removeCls('masked', prefix);
            maskEl.hide();
        }
    },

    updateMaskText: function(newMaskText) {
        var me = this,
            maskEl      = me.maskEl,
            maskTextCls = me.maskTextCls,
            el;

        if (newMaskText && maskEl) {
            el = maskEl.down('.x-mask-msg');
            el.update(newMaskText);

            maskEl.addCls('x-mask-text');
        } else if (maskEl) {
            maskEl.removeCls('x-mask-text');
        }

        this.maskText = newMaskText;
    },

    updateLoadingMask: function(newLoadingMask) {
        var maskText = this.maskText,
            maskEl = this.maskEl;

        if (newLoadingMask && maskEl) {
            maskEl.addCls('x-mask-loading');
        } else if (maskEl) {
            maskEl.removeCls('x-mask-loading');
        }

        this.loadingMask = newLoadingMask;
    },

    mask: function(msg, msgCls, loadingMask) {
        this.updateLoadingMask(loadingMask);
        this.updateMaskText(msg);
        this.updateMasked(true);
    },

    unmask: function() {
        this.updateMasked(false);
    },

    
    destroy: function() {
        this.callParent();

        var parent = this.getParent(),
            referenceList = this.referenceList,
            i, ln, reference;

        
        if (parent) {
            parent.remove(this, false);
        }

        
        for (i = 0,ln = referenceList.length; i < ln; i++) {
            reference = referenceList[i];
            this[reference].destroy();
            delete this[reference];
        }

        Ext.ComponentManager.unregister(this);
    }

}, function() {
    Ext.LoadingSpinner = '<div class="x-loading-spinner"><span class="x-loading-top"></span><span class="x-loading-right"></span><span class="x-loading-bottom"></span><span class="x-loading-left"></span></div>';

    var emptyFn = Ext.emptyFn;

    this.override({
        constructor: function(config) {
            var name;

            if (config) {
                if (config.enabled) {
                    Ext.Logger.deprecate("'enabled' config is deprecated, please use 'disabled' config instead", this);
                    config.disabled = !config.enabled;
                }

                if (config.scroll || this.config.scroll) {
                    Ext.Logger.deprecate("'scroll' config is deprecated, please use 'scrollable' config instead", this);
                    config.scrollable = config.scroll || this.config.scroll;
                }

                
                if (config.componentCls) {
                    Ext.Logger.deprecate("'componentCls' config is deprecated, please use 'cls' config instead", this);
                    config.cls = config.componentCls;
                }

                for (name in config) {
                    if (config.hasOwnProperty(name) && name !== 'xtype' && name !== 'xclass' && !this.hasConfig(name)) {
                        this[name] = config[name];
                    }
                }
            }

            this.callParent(arguments);

            if (this.onRender !== emptyFn) {
                Ext.Logger.deprecate("onRender() is deprecated, please put your code inside initialize() instead", this);
                this.onRender();
            }

            if (this.afterRender !== emptyFn) {
                Ext.Logger.deprecate("afterRender() is deprecated, please put your code inside initialize() instead", this);
                this.afterRender();
            }

            if (this.initEvents !== emptyFn) {
                Ext.Logger.deprecate("initEvents() is deprecated, please put your code inside initialize() instead", this);
                this.initEvents();
            }

            if (this.initComponent !== emptyFn) {
                Ext.Logger.deprecate("initComponent() is deprecated, please put your code inside initialize() instead", this);
                this.initComponent();
            }
        },

        onRender: emptyFn,

        afterRender: emptyFn,

        initEvents: emptyFn,

        initComponent: emptyFn,

        show: function() {
            if (this.renderElement.dom) {
                var containerDom = this.renderElement.dom.parentNode;

                if (containerDom && containerDom.nodeType == 11) {
                    Ext.Logger.deprecate("Showing a component that currently doesn't have any container, " +
                        "please use Ext.Viewport.add() to add this component to the viewport", this);
                    Ext.Viewport.add(this);
                }
            }

            return this.callParent(arguments);
        },

        doSetHidden: function(hidden) {
            this.callParent(arguments);

            this.fireEvent(hidden ? 'hide' : 'show', this);
        }
    });

    

    Ext.deprecateClassMembers(this, {
        el: 'element',
        body: 'element',
        outer: 'renderElement',
        ownerCt: 'parent',
        update: 'setHtml'
    });
});

})(Ext.baseCSSPrefix);


Ext.define('Ext.Button', {
    extend: 'Ext.Component',

    xtype: 'button',

    cachedConfig: {
        
        pressedCls: Ext.baseCSSPrefix + 'button-pressed',

        
        badgeCls: Ext.baseCSSPrefix + 'badge',

        
        hasBadgeCls: Ext.baseCSSPrefix + 'hasbadge',

        
        labelCls: Ext.baseCSSPrefix + 'button-label',

        
        iconMaskCls: Ext.baseCSSPrefix + 'icon-mask'
    },

    config: {
        
        badgeText: null,

        
        text: null,

        
        iconCls: null,

        
        icon: null,

        
        iconAlign: 'left',

        pressedDelay: 0,

        
        iconMask: null,

        
        handler: null,

        
        scope: null,

        
        autoEvent: null,

        baseCls: Ext.baseCSSPrefix + 'button',

        
        ui: 'normal'
    },

    template: [
        {
            tag: 'span',
            reference: 'badgeElement',
            hidden: true
        },

        {
            tag: 'span',
            className: Ext.baseCSSPrefix + 'button-icon',
            reference: 'iconElement',
            hidden: true
        },
        {
            tag: 'span',
            reference: 'textElement',
            hidden: true
        }
    ],

    initialize: function() {
        this.element.on({
            scope      : this,
            tap        : 'onTap',
            touchstart : 'onPress',
            touchmove  : 'onRelease',
            touchend   : 'onRelease'
        });
    },

    
    updateBadgeText: function(badgeText) {
        var element = this.element,
            badgeElement = this.badgeElement;

        if (badgeText) {
            badgeElement.show();
            badgeElement.setText(badgeText);
        }
        else {
            badgeElement.hide();
        }

        element[(badgeText) ? 'addCls' : 'removeCls'](this.getHasBadgeCls());
    },

    
    updateText: function(text) {
        var element = this.textElement;

        if (text) {
            element.show();
            element.setText(text);
        }
        else {
            element.hide();
        }
    },

    
    updateBadgeCls: function(badgeCls, oldBadgeCls) {
        this.badgeElement.replaceCls(oldBadgeCls, badgeCls);
    },

    
    updateHasBadgeCls: function(hasBadgeCls, oldHasBadgeCls) {
        var element = this.element;

        if (element.hasCls(oldHasBadgeCls)) {
            element.replaceCls(oldHasBadgeCls, hasBadgeCls);
        }
    },

    
    updateLabelCls: function(labelCls, oldLabelCls) {
        this.textElement.replaceCls(oldLabelCls, labelCls);
    },

    
    updatePressedCls: function(pressedCls, oldPressedCls) {
        var element = this.element;

        if (element.hasCls(oldPressedCls)) {
            element.replaceCls(oldPressedCls, pressedCls);
        }
    },

    
    updateIcon: function(icon) {
        var element = this.iconElement;

        if (icon) {
            element.show();
            element.setStyle('background-image', icon ? 'url(' + icon + ')' : '');
            this.refreshIconAlign();
            this.refreshIconMask();
        }
        else {
            element.hide();
            this.setIconAlign(false);
        }
    },

    
    updateIconCls: function(iconCls, oldIconCls) {
        var element = this.iconElement;

        if (iconCls) {
            element.show();
            element.replaceCls(oldIconCls, iconCls);
            this.refreshIconAlign();
            this.refreshIconMask();
        }
        else {
            element.hide();
            this.setIconAlign(false);
        }
    },

    
    updateIconAlign: function(alignment, oldAlignment) {
        var element = this.element,
            baseCls = Ext.baseCSSPrefix + 'iconalign-';

        if (!this.getText()) {
            alignment = "center";
        }

        element.removeCls(baseCls + "center");
        element.removeCls(baseCls + oldAlignment);
        if (this.getIcon() || this.getIconCls()) {
            element.addCls(baseCls + alignment);
        }
    },

    refreshIconAlign: function() {
        this.updateIconAlign(this.getIconAlign());
    },

    
    updateIconMaskCls: function(iconMaskCls, oldIconMaskCls) {
        var element = this.iconElement;

        if (this.getIconMask()) {
            element.replaceCls(oldIconMaskCls, iconMaskCls);
        }
    },

    
    updateIconMask: function(iconMask) {
        this.iconElement[iconMask ? "addCls" : "removeCls"](this.getIconMaskCls());
    },

    refreshIconMask: function() {
        this.updateIconMask(this.getIconMask());
    },

    applyAutoEvent: function(autoEvent) {
        var me = this;

        if (typeof autoEvent == 'string') {
            autoEvent = {
                name : autoEvent,
                scope: me.scope || me
            };
        }

        return autoEvent;
    },

    
    updateAutoEvent: function(autoEvent) {
        var name  = autoEvent.name,
            scope = autoEvent.scope;

        this.setHandler(function() {
            scope.fireEvent(name, scope, this);
        });

        this.setScope(scope);
    },

    applyPressedDelay: function(delay) {
        return isNaN(delay) ? 0 : delay;
    },

    
    onPress: function() {
        var element = this.element,
            pressedDelay = this.getPressedDelay(),
            pressedCls = this.getPressedCls();

        if (!this.getDisabled()) {
            this.isPressed = true;

            if (this.hasOwnProperty('releasedTimeout')) {
                clearTimeout(this.releasedTimeout);
                delete this.releasedTimeout;
            }

            if (pressedDelay > 0) {
                this.pressedTimeout = setTimeout(function() {
                    element.addCls(pressedCls);
                }, pressedDelay);
            }
            else {
                element.addCls(pressedCls);
            }
        }
    },

    
    onRelease: function(e) {
        this.fireAction('release', [this, e], 'doRelease');
    },

    
    doRelease: function(e) {
        var me = this;

        if (!me.isPressed) {
            return;
        }

        me.isPressed = true;

        if (this.hasOwnProperty('pressedTimeout')) {
            clearTimeout(this.pressedTimeout);
            delete this.pressedTimeout;
        }

        me.releasedTimeout = setTimeout(function() {
            if (me && me.element) {
                me.element.removeCls(me.getPressedCls());
            }
        }, 10);
    },

    
    onTap: function(e) {
        if (this.getDisabled()) {
            return false;
        }

        this.fireAction('tap', [this, e], 'doTap');
    },

    
    doTap: function() {
        var handler = this.getHandler(),
            scope   = this.getScope() || this;

        if (!handler) {
            return;
        }

        if (typeof handler == 'string') {
            handler = scope[handler];
        }

        handler.apply(scope, arguments);
    }
}, function() {

    
    Ext.deprecateClassMethod(this, 'setBadge', this.prototype.setBadgeText, "'setBadge()' is deprecated, please use setBadgeText()");

    
    Ext.deprecateClassMethod(this, 'setIconClass', this.prototype.setIconCls, "'setIconClass()' is deprecated, please use setIconCls()");

    this.override({
        constructor: function(config) {
            if (config) {
                
                if (config.hasOwnProperty('badge')) {
                    Ext.Logger.deprecate("'badge' config is deprecated, please use 'badgeText' config instead", this);
                    config.badgeText = config.badge;
                }

                
                if (config.hasOwnProperty('html')) {
                    Ext.Logger.deprecate("'html' config is deprecated, please use 'text' config instead", this);
                    config.text = config.html;
                }
            }

            this.callParent([config]);
        }
    });

});


Ext.define('Ext.Img', {
    extend: 'Ext.Component',
    xtype : 'image',

    config: {
        
        src: null,

        
        baseCls: Ext.baseCSSPrefix + 'img'
    },

    initialize: function() {
        var me = this;
        me.callParent(arguments);
        me.relayEvents(me.renderElement, '*');
    },

    onAfterRender: function() {
        this.updateSrc(this.getSrc());
    },

    
    updateSrc: function(newSrc) {
        var renderElement = this.renderElement;
        if (renderElement) {
            renderElement.dom.style.backgroundImage = newSrc ? 'url(' + newSrc + ')' : '';
        }
    }
});

Ext.define('Ext.Label', {
    extend: 'Ext.Component',
    xtype: 'label',

    config: {
        
    }
});

Ext.define('Ext.Map', {
    extend: 'Ext.Component',
    xtype : 'map',
    requires: ['Ext.util.GeoLocation'],

    config: {
        

        

        

        

        
        baseCls: Ext.baseCSSPrefix + 'map',

        
        useCurrentLocation: false,

        
        map: null,

        
        geo: null,

        
        maskMap: false,

        
        maskMapCls: Ext.baseCSSPrefix + 'mask-map',

        
        mapOptions: {}
    },

    constructor: function() {
        this.callParent(arguments);
        this.element.setVisibilityMode(Ext.Element.OFFSETS);

        if (!(window.google || {}).maps) {
            this.setHtml('Google Maps API is required');
        }
    },

    updateUseCurrentLocation: function(useCurrentLocation) {
        this.setGeo(useCurrentLocation);
        if (!useCurrentLocation) {
            this.renderMap();
        }
    },

    updateMaskMap: function(maskMap) {
        if (maskMap) {
            this.element.mask(null, this.getMaskMapCls());
        }
        else {
            this.element.unmask();
        }
    },

    applyGeo: function(config) {
        return Ext.factory(config, Ext.util.GeoLocation, this.getGeo());
    },

    updateGeo: function(newGeo, oldGeo) {
        var events = {
            locationupdate : 'onGeoUpdate',
            locationerror : 'onGeoError',
            scope : this
        };

        if (oldGeo) {
            oldGeo.un(events);
        }

        if (newGeo) {
            newGeo.on(events);
            newGeo.updateLocation();
        }
    },

    
    renderMap: function() {
        var me = this,
            gm = (window.google || {}).maps,
            element = me.element,
            mapOptions = me.getMapOptions(),
            map = me.getMap(),
            event;

        if (gm) {
            if (Ext.is.iPad) {
                Ext.merge(mapOptions, {
                    navigationControlOptions: {
                        style: gm.NavigationControlStyle.ZOOM_PAN
                    }
                });
            }

            Ext.merge(mapOptions, {
                center: new gm.LatLng(37.381592, -122.135672), 
                zoom: 12,
                mapTypeId: gm.MapTypeId.ROADMAP
            });

            if (element.dom.firstChild) {
                Ext.fly(element.dom.firstChild).remove();
            }

            if (map) {
                gm.event.clearInstanceListeners(map);
            }

            me.setMap(new gm.Map(element.dom, mapOptions));
            map = me.getMap();

            
            event = gm.event;
            event.addListener(map, 'zoom_changed', Ext.bind(me.onZoomChange, me));
            event.addListener(map, 'maptypeid_changed', Ext.bind(me.onTypeChange, me));
            event.addListener(map, 'center_changed', Ext.bind(me.onCenterChange, me));

            me.fireEvent('maprender', me, map);
        }
    },

    
    onGeoUpdate: function(geo) {
        var center;
        if (geo) {
            center = this.getMapOptions().center = new google.maps.LatLng(geo.getLatitude(), geo.getLongitude());
        }

        this.setHtml(center);
    },

    
    onGeoError: Ext.emptyFn,

    
    onUpdate: function(map, e, options) {
        this.setHtml((options || {}).data);
    },

    
    update: function(coordinates) {
        var me = this,
            map = me.getMap(),
            gm = (window.google || {}).maps;

        if (gm) {
            coordinates = coordinates || new gm.LatLng(37.381592, -122.135672);

            if (coordinates && !(coordinates instanceof gm.LatLng) && 'longitude' in coordinates) {
                coordinates = new gm.LatLng(coordinates.latitude, coordinates.longitude);
            }

            if (!map) {
                me.renderMap();
                map = me.getMap();
            }

            if (map && coordinates instanceof gm.LatLng) {
                map.panTo(coordinates);
            }
        }
    },

    
    onZoomChange : function() {
        var mapOptions = this.getMapOptions(),
            map = this.getMap();

        mapOptions.zoom = (map && map.getZoom) ? map.getZoom() : mapOptions.zoom || 10;

        this.fireEvent('zoomchange', this, map, mapOptions.zoom);
    },

    
    onTypeChange : function() {
        var mapOptions = this.getMapOptions(),
            map = this.getMap();

        mapOptions.mapTypeId = (map && map.getMapTypeId) ? map.getMapTypeId() : mapOptions.mapTypeId;

        this.fireEvent('typechange', this, map, mapOptions.mapTypeId);
    },

    
    onCenterChange: function() {
        var mapOptions = this.getMapOptions(),
            map = this.getMap();

        mapOptions.center = (map && map.getCenter) ? map.getCenter() : mapOptions.center;

        this.fireEvent('centerchange', this, map, mapOptions.center);

    },

    
    onDestroy: function() {
        Ext.destroy(this.getGeo());
        var map = this.getMap();

        if (this.getMaskMap() && this.mask) {
            this.element.unmask();
        }

        if (map && (window.google || {}).maps) {
            google.maps.event.clearInstanceListeners(map);
        }

        this.callParent();
    }
}, function() {
    
    Ext.deprecateClassMethod(this, 'getState', 'getMapOptions');
});

Ext.define('Ext.Mask', {
    extend: 'Ext.Button',
    xtype: 'mask',

    config: {
        baseCls: 'x-mask',
        pressedCls: 'x-mask-pressed',
        hidden: true,
        top: 0,
        left: 0,
        right: 0,
        bottom: 0
    }
});

Ext.define('Ext.Media', {
    extend: 'Ext.Component',
    xtype: 'media',

    config: {
        
        url: '',

        
        enableControls: Ext.os.is.Android ? false : true,

        
        autoResume: false,

        
        autoPause: true,

        
        preload: true,

        
        loop: false,

        
        media: null,

        
        playing: false
    },

    initialize: function() {
        var me = this;
        me.callParent(arguments);

        me.on({
            scope: me,

            activate  : me.onActivate,
            deactivate: me.onDeactivate
        });
    },

    
    isPlaying: function() {
        return this.getPlaying();
    },

    
    onActivate: function() {
        var me = this;

        if (me.getAutoResume() && !me.isPlaying()) {
            me.play();
        }
    },

    
    onDeactivate: function() {
        var me = this;

        if (me.getAutoResume() && me.isPlaying()) {
            me.pause();
        }
    },

    
    updateUrl: function(newUrl) {
        var dom = this.media.dom;

        
        
        dom.src = newUrl;
        dom.load();

        if (this.getPlaying()) {
            this.play();
        }
    },

    
    updateEnableControls: function(enableControls) {
        this.media.dom.controls = enableControls ? 'controls' : false;
    },

    
    updateLoop: function(loop) {
        this.media.dom.loop = loop ? 'loop' : false;
    },

    
    play: function() {
        this.media.dom.play();
        this.setPlaying(true);
    },

    
    pause: function() {
        this.media.dom.pause();
        this.setPlaying(false);
    },

    
    toggle: function() {
        this.isPlaying() ? this.pause() : this.play();
    }
});


Ext.define('Ext.Audio', {
    extend: 'Ext.Media',
    xtype : 'audio',

    config: {
        
        cls: Ext.baseCSSPrefix + 'audio'

        
    },

    
    onActivate: function() {
        var me = this;

        me.callParent();
        
        if (Ext.is.Phone) {
            me.element.show();
        }
    },

    
    onDeactivate: function() {
        var me = this;

        me.callParent();

        if (Ext.is.Phone) {
            me.element.hide();
        }
    },

    getTemplate: function() {
        var clsPrefix = Ext.baseCSSPrefix;

        if (Ext.feature.has.Audio) {
            return [{
                reference: 'media',
                tag: 'audio',
                classList: [clsPrefix + 'component']
            }];
        } else {
            return [{
                reference: 'media',
                tag: 'audio',
                classList: [clsPrefix + 'component']
            }];

            
            
            
            
            
            
            
            
            
            
            
            
        }
    }
});


Ext.define('Ext.Spacer', {
    extend: 'Ext.Component',
    alias : 'widget.spacer',

    config: {
        
        
        
    },

    
    constructor: function(config) {
        config = config || {};

        if (!config.width) {
            config.flex = 1;
        }

        this.callParent([config]);
    }
});


Ext.define('Ext.Title', {
    extend: 'Ext.Component',
    xtype: 'title',
    
    config: {
        
        baseCls: 'x-title',

        
        title: '&nbsp'
    },

    
    updateTitle: function(newTitle) {
        this.setHtml(newTitle);
    }
});

Ext.define('Ext.Video', {
    extend: 'Ext.Media',
    xtype: 'video',

    config: {
        

        
        posterUrl: null,

        
        cls: Ext.baseCSSPrefix + 'video'
    },

    initialize: function() {
        this.callParent();
        if (Ext.os.is.Android) {
            var ghost = this.ghost = this.element.append(Ext.Element.create({
                cls: Ext.baseCSSPrefix + 'video-ghost',
                style: 'background-image: url(' + this.getPosterUrl() + ');'
            }));
            ghost.on({
                tap: 'onGhostTap',
                scope: this
            });
        }
    },

    
    onGhostTap: function() {

        
        
        

        var me = this;
        setTimeout(function() {
            me.play();
            me.media.hide();
        }, 200);
    },

    template: [{
        tag: 'video',
        reference: 'media',
        classList: [Ext.baseCSSPrefix + 'media']
    }],

    
    updatePosterUrl: function(newUrl) {
        var ghost = this.ghost;
        if (ghost) {
            ghost.dom.style.backgroundImage = newUrl ? 'url(' + newUrl + ')' : '';
        }
    }
});


Ext.define('Ext.carousel.Indicator', {
    extend: 'Ext.Component',
    xtype : 'carouselindicator',
    alternateClassName: 'Ext.Carousel.Indicator',

    config: {
        
        baseCls: Ext.baseCSSPrefix + 'carousel-indicator',
        direction: 'horizontal'
    },

    initialize: function() {
        var me = this;
        me.callParent(arguments);

        me.indicators = [];

        me.element.on({
            tap  : 'onTap',
            scope: this
        });
    },

    updateDirection: function(newDirection, oldDirection) {
        var baseCls = this.getBaseCls();

        this.element.replaceCls(baseCls + '-' + oldDirection, baseCls + '-' + newDirection);

        if (newDirection === 'horizontal') {
            this.setBottom(0);
            this.setRight(null);
        } else {
            this.setRight(0);
            this.setBottom(null);
        }
    },

    addIndicator: function() {
        this.indicators.push(this.element.createChild({
            tag: 'span'
        }));
    },

    removeIndicator: function() {
        if (this.indicators.length) {
            this.indicators.pop().remove();
        }
    },

    setActiveIndex: function(index) {
        var indicators = this.indicators;

        if (indicators.length && indicators[index]) {
            indicators[index].radioCls(this.getBaseCls() + '-active');
        }
    },

    
    onTap: function(e, t) {
        var box = this.element.getPageBox(),
            centerX = box.left + (box.width / 2),
            centerY = box.top + (box.height / 2),
            direction = this.getDirection();

        if ((direction === 'horizontal' && e.pageX > centerX) || (direction === 'vertical' && e.pageY > centerY)) {
            this.fireEvent('next');
        } else {
            this.fireEvent('previous');
        }
    }
});

Ext.define('Ext.dataview.IndexBar', {
    extend: 'Ext.Component',
    alternateClassName: 'Ext.IndexBar',

    config: {
        baseCls: Ext.baseCSSPrefix + 'indexbar',

        
        direction: 'vertical',

        
        letters: ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'],

        ui: 'alphabet',

        
        listPrefix: null
    },

    
    itemCls: Ext.baseCSSPrefix + '',

    updateDirection: function(newDirection, oldDirection) {
        var baseCls = this.getBaseCls();

        this.element.replaceCls(baseCls + '-' + oldDirection, baseCls + '-' + newDirection);
    },

    getElementConfig: function() {
        return {
            reference: 'wrapper',
            classList: ['x-centered', 'x-indexbar-wrapper'],
            children: [this.callParent()]
        };
    },

    updateLetters: function(letters) {
        this.innerElement.setHtml('');

        if (letters) {
            var ln = letters.length,
                i;

            for (i = 0; i < ln; i++) {
                this.innerElement.createChild({
                    html: letters[i]
                });
            }
        }
    },

    updateListPrefix: function(listPrefix) {
        if (listPrefix && listPrefix.length) {
            this.innerElement.createChild({
                html: listPrefix
            }, 0);
        }
    },

    
    initialize: function() {
        this.callParent();

        this.innerElement.on({
            touchstart: this.onTouchStart,
            touchend: this.onTouchEnd,
            touchmove: this.onTouchMove,
            scope: this
        });
    },

    
    onTouchStart: function(e, t) {
        e.stopPropagation();
        this.innerElement.addCls(this.getBaseCls() + '-pressed');
        this.pageBox = this.innerElement.getPageBox();
        this.onTouchMove(e);
    },

    
    onTouchEnd: function(e, t) {
        this.innerElement.removeCls(this.getBaseCls() + '-pressed');
    },

    
    onTouchMove: function(e) {
        var point = Ext.util.Point.fromEvent(e),
            target,
            record,
            pageBox = this.pageBox;

        if (!pageBox) {
            pageBox = this.pageBox = this.el.getPageBox();
        }

        if (this.getDirection() === 'vertical') {
            if (point.y > pageBox.bottom || point.y < pageBox.top) {
                return;
            }
            target = Ext.Element.fromPoint(pageBox.left + (pageBox.width / 2), point.y);
        }
        else {
            if (point.x > pageBox.right || point.x < pageBox.left) {
                return;
            }
            target = Ext.Element.fromPoint(point.x, pageBox.top + (pageBox.height / 2));
        }

        if (target) {
            
            this.fireEvent('index', target.dom.innerHTML, target);
        }
    }
});

Ext.define('Ext.dataview.ListDisclosure', {
    extend: 'Ext.Component',
    xtype : 'listdisclosure',

    config: {
        
        baseCls: Ext.baseCSSPrefix + 'list-disclosure'
    },

    initialize: function() {
        var me = this;

        me.callParent();

        me.element.on({
            tap: 'onTap',
            scope: me
        });
    },

    onTap: function(e) {
        this.fireEvent('tap', this, e);
    }
});


Ext.define('Ext.dataview.ListIcon', {
    extend: 'Ext.Img',
    xtype : 'listicon',

    config: {
        
        baseCls: Ext.baseCSSPrefix + 'list-icon'
    }
});

Ext.define('Ext.dataview.ListItemHeader', {
    extend: 'Ext.Component',
    xtype : 'listitemheader',

    config: {
        
        baseCls: Ext.baseCSSPrefix + 'list-header',
        docked: 'top'
    }
});
Ext.define('Ext.event.publisher.ComponentDelegation', {
    extend: 'Ext.event.publisher.Publisher',

    requires: [
        'Ext.Component',
        'Ext.ComponentQuery'
    ],

    targetType: 'component',

    optimizedSelectorRegex: /^#([\w\-]+)((?:[\s]*)>(?:[\s]*)|(?:\s*))([\w\-]+)$/i,

    idSelectorRegex: /^#([\w\-]+)$/i,

    handledEvents: ['*'],

    constructor: function() {
        this.subscribers = {};

        return this.callParent(arguments);
    },

    getSubscribers: function(eventName, createIfNotExist) {
        var subscribers = this.subscribers,
            eventSubscribers = subscribers[eventName];

        if (!eventSubscribers && createIfNotExist) {
            eventSubscribers = subscribers[eventName] = {
                type: {
                    $length: 0
                },
                selector: [],
                $length: 0
            }
        }

        return eventSubscribers;
    },

    subscribe: function(target, eventName) {
        
        if (this.idSelectorRegex.test(target)) {
            return false;
        }

        var optimizedSelector = target.match(this.optimizedSelectorRegex),
            subscribers = this.getSubscribers(eventName, true),
            typeSubscribers = subscribers.type,
            selectorSubscribers = subscribers.selector,
            id, isDescendant, type, map, subMap;

        if (optimizedSelector !== null) {
            id = optimizedSelector[1];
            isDescendant = optimizedSelector[2].indexOf('>') === -1;
            type = optimizedSelector[3];

            map = typeSubscribers[type];

            if (!map) {
                map = typeSubscribers[type] = {
                    descendents: {
                        $length: 0
                    },
                    children: {
                        $length: 0
                    },
                    $length: 0
                }
            }

            subMap = isDescendant ? map.descendents : map.children;

            if (subMap[id]) {
                return true;
            }

            subMap[id] = true;
            subMap.$length++;
            map.$length++;
            typeSubscribers.$length++;
        }
        else {
            if (selectorSubscribers[target]) {
                return true;
            }

            selectorSubscribers[target] = true;
            selectorSubscribers.push(target);
        }

        subscribers.$length++;

        return true;
    },

    unsubscribe: function(target, eventName) {
        var subscribers = this.getSubscribers(eventName);

        if (!subscribers) {
            return false;
        }

        var match = target.match(this.optimizedSelectorRegex),
            typeSubscribers = subscribers.type,
            selectorSubscribers = subscribers.selector,
            id, isDescendant, type, map, subMap;

        if (match !== null) {
            id = match[1];
            isDescendant = match[2].indexOf('>') === -1;
            type = match[3];

            map = typeSubscribers[type];

            if (!map) {
                return true;
            }

            subMap = isDescendant ? map.descendents : map.children;

            if (subMap[id]) {
                delete subMap[id];
                subMap.$length--;
                map.$length--;
                typeSubscribers.$length--;
            }
        }
        else {
            if (!selectorSubscribers[target]) {
                return true;
            }

            delete selectorSubscribers[target];
            Ext.Array.remove(selectorSubscribers, target);
        }

        subscribers.$length--;

        return true;
    },

    notify: function(target, eventName) {
        var subscribers = this.getSubscribers(eventName),
            id, component;

        if (!subscribers || subscribers.$length === 0) {
            return false;
        }

        id = target.substr(1);
        component = Ext.ComponentManager.get(id);

        if (component && component.hasParent()) {
            this.dispatcher.doAddListener(this.targetType, target, eventName, 'publish', this, {
                args: [eventName, component]
            }, 'before');
        }
    },

    matchesSelector: function(component, selector) {
        return Ext.ComponentQuery.is(component, selector);
    },

    dispatch: function(target, eventName, args, connectedController) {
        this.dispatcher.doDispatchEvent(this.targetType, target, eventName, args, [], connectedController);
    },

    publish: function(eventName, component) {
        var eventController = arguments[arguments.length - 1],
            subscribers = this.getSubscribers(eventName),
            typeSubscribers = subscribers.type,
            selectorSubscribers = subscribers.selector,
            args = Array.prototype.slice.call(arguments, 2, -2),
            types = component.xtypesChain,
            descendentsSubscribers, childrenSubscribers,
            parentId, ancestorIds, ancestorId, parentComponent,
            selector,
            i, ln, type, j, subLn;

        for (i = 0,ln = types.length; i < ln; i++) {
            type = types[i];

            subscribers = typeSubscribers[type];

            if (subscribers && subscribers.$length > 0) {
                descendentsSubscribers = subscribers.descendents;

                if (descendentsSubscribers.$length > 0) {
                    if (!ancestorIds) {
                        ancestorIds = component.getAncestorIds();
                    }

                    for (j = 0,subLn = ancestorIds.length; j < subLn; j++) {
                        ancestorId = ancestorIds[j];

                        if (descendentsSubscribers[ancestorId] === true) {
                            this.dispatch('#' + ancestorId + ' ' + type, eventName, args, eventController);
                        }

                    }
                }

                childrenSubscribers = subscribers.children;

                if (childrenSubscribers.$length > 0) {
                    if (!parentId) {
                        if (ancestorIds) {
                            parentId = ancestorIds[0];
                        }
                        else {
                            
                            parentComponent = component.getParent();
                            if (parentComponent) {
                                parentId = parentComponent.getId();
                            }
                        }
                    }

                    if (parentId) {
                        if (childrenSubscribers[parentId] === true) {
                            this.dispatch('#' + parentId + ' > ' + type, eventName, args, eventController);
                        }
                    }
                }
            }
        }

        ln = selectorSubscribers.length;

        if (ln > 0) {
            for (i = 0; i < ln; i++) {
                selector = selectorSubscribers[i];

                if (this.matchesSelector(component, selector)) {
                    this.dispatch(selector, eventName, args, eventController);
                }
            }
        }
    }
});


Ext.define('Ext.field.Input', {
    extend: 'Ext.Component',
    xtype : 'input',

    cachedConfig: {
        
        inputCls: Ext.baseCSSPrefix + 'form-field',

        
        focusCls: Ext.baseCSSPrefix + 'field-focus',

        
        maskCls: Ext.baseCSSPrefix + 'field-mask',
        
        
        useMask: 'auto',

        
        type: 'text',

        
        checked: false
    },

    config: {
        
        baseCls: Ext.baseCSSPrefix + 'field-input',

        
        tag: 'input',

        
        name: null,

        
        value: null,
        
        
        isFocused: false,

        
        tabIndex: null,

        
        placeHolder: null,

        
        minValue: null,

        
        maxValue: null,

        
        stepValue: null,

        
        maxLength: null,

        
        autoComplete: null,

        
        autoCapitalize: null,

        
        autoCorrect: null,

        
        readOnly: null,

        
        maxRows: null,

        

        
        startValue: false
    },

    
    originalValue: undefined,

    
    getTemplate: function() {
        var items = [
            {
                reference: 'input',
                tag: this.getTag()
            },
            {
                reference: 'clearIcon',
                cls: 'x-clear-icon',
                html: 'x'
            }
        ];

        items.push({
            reference: 'mask',
            classList: [this.getMaskCls()]
        });

        return items;
    },

    
    initElement: function() {
        var me = this;

        me.callParent();

        me.input.on({
            scope: me,

            keyup    : 'onKeyUp',
            focus    : 'onFocus',
            blur     : 'onBlur',
            paste    : 'onPaste'
            
            
        });

        me.mask.on({
            tap: 'onMaskTap',
            scope: me
        });

        if (me.clearIcon) {
            me.clearIcon.on({
                tap: 'onClearIconTap',
                scope: me
            });
        }

        me.doInitValue();
    },

    
    doInitValue: function() {
        
        this.originalValue = this.getValue();
    },

    applyUseMask: function(useMask) {
        if (useMask === 'auto') {
            useMask = Ext.os.is.iOS && Ext.os.version.lt('5');
        }

        return Boolean(useMask);
    },

    
    updateUseMask: function(newUseMask) {
        this.mask[newUseMask ? 'show' : 'hide']();
    },

    
    updateFieldAttribute: function(attribute, newValue) {
        var input = this.input;

        if (newValue) {
            input.dom.setAttribute(attribute, newValue);
        } else {
            input.dom.removeAttribute(attribute);
        }
    },

    
    updateInputCls: function(newInputCls, oldInputCls) {
        this.input.addCls(Ext.baseCSSPrefix + 'input-el');
        this.input.replaceCls(oldInputCls, newInputCls);
    },

    
    updateType: function(newType, oldType) {
        var prefix = Ext.baseCSSPrefix + 'input-';

        this.input.replaceCls(prefix + oldType, prefix + newType);
        this.updateFieldAttribute('type', newType);
    },

    
    updateName: function(newName) {
        this.updateFieldAttribute('name', newName);
    },

    
    getValue: function() {
        var input = this.input;

        if (input) {
            this._value = input.dom.value;
        }

        return this._value;
    },

    
    applyValue: function(value) {
        return (Ext.isEmpty(value)) ? '' : value;
    },

    
    updateValue: function(newValue) {
        var input = this.input;

        if (input) {
            input.dom.value = newValue;
        }
    },

    setValue: function(newValue) {
        this.updateValue(this.applyValue(newValue));
        return this;
    },

    
    applyTabIndex: function(tabIndex) {
        if (tabIndex !== null && typeof tabIndex != 'number') {
            throw new Error("Ext.field.Field: [applyTabIndex] trying to pass a value which is not a number");
        }
        return tabIndex;
    },

    
    updateTabIndex: function(newTabIndex) {
        this.updateFieldAttribute('tabIndex', newTabIndex);
    },

    
    testAutoFn: function(value) {
        return [true, 'on'].indexOf(value) !== -1;
    },

    applyMaxLength: function(maxLength) {
        if (maxLength !== null && typeof maxLength != 'number') {
            throw new Error("Ext.field.Text: [applyMaxLength] trying to pass a value which is not a number");
        }
        return maxLength;
    },

    
    updateMaxLength: function(newMaxLength) {
        this.updateFieldAttribute('maxlength', newMaxLength);
    },

    
    updatePlaceHolder: function(newPlaceHolder) {
        this.updateFieldAttribute('placeholder', newPlaceHolder);
    },

    
    applyAutoComplete: function(autoComplete) {
        return this.testAutoFn(autoComplete);
    },

    
    updateAutoComplete: function(newAutoComplete) {
        var value = newAutoComplete ? 'on' : 'off';
        this.updateFieldAttribute('autocomplete', value);
    },

    
    applyAutoCapitalize: function(autoCapitalize) {
        return this.testAutoFn(autoCapitalize);
    },

    
    updateAutoCapitalize: function(newAutoCapitalize) {
        var value = newAutoCapitalize ? 'on' : 'off';
        this.updateFieldAttribute('autocapitalize', value);
    },

    
    applyAutoCorrect: function(autoCorrect) {
        return this.testAutoFn(autoCorrect);
    },

    
    updateAutoCorrect: function(newAutoCorrect) {
        var value = newAutoCorrect ? 'on' : 'off';
        this.updateFieldAttribute('autocorrect', value);
    },

    
    updateMinValue: function(newMinValue) {
        this.updateFieldAttribute('min', newMinValue);
    },

    
    updateMaxValue: function(newMaxValue) {
        this.updateFieldAttribute('max', newMaxValue);
    },

    
    updateStepValue: function(newStepValue) {
        this.updateFieldAttribute('step', newStepValue);
    },

    
    checkedRe: /^(true|1|on)/i,

    
    getChecked: function() {
        var el = this.input,
            checked;

        if (el) {
            checked = el.dom.checked;
            this._checked = checked;
        }

        return checked;
    },

    
    applyChecked: function(checked) {
        return !!this.checkedRe.test(String(checked));
    },

    
    updateChecked: function(newChecked) {
        this.input.dom.checked = newChecked;
    },

    
    updateReadOnly: function(readOnly) {
        this.updateFieldAttribute('readonly', readOnly);
    },

    
    applyMaxRows: function(maxRows) {
        if (maxRows !== null && typeof maxRows !== 'number') {
            throw new Error("Ext.field.Input: [applyMaxRows] trying to pass a value which is not a number");
        }
    },

    updateMaxRows: function(newRows) {
        this.updateFieldAttribute('rows', newRows);
    },

    
    doSetDisabled: function(disabled) {
        this.callParent(arguments);

        this.input.dom.disabled = disabled;

        if (!disabled) {
            this.blur();
        }
    },

    
    isDirty: function() {
        if (this.getDisabled()) {
            return false;
        }

        return String(this.getValue()) !== String(this.originalValue);
    },

    
    reset: function() {
        this.setValue(this.originalValue);
    },

    
    onClearIconTap: function(e) {
        this.fireEvent('clearicontap', this, e);
    },

    
    onMaskTap: function(e) {
        this.fireAction('masktap', [this, e], 'doMaskTap');
    },

    
    doMaskTap: function() {
        if (this.getDisabled()) {
            return false;
        }

        this.maskCorrectionTimer = Ext.defer(this.showMask, 1000, this);
        this.hideMask();
    },

    
    showMask: function(e) {
        if (this.mask) {
            this.mask.setStyle('display', 'block');
        }
    },

    
    hideMask: function(e) {
        if (this.mask) {
            this.mask.setStyle('display', 'none');
        }
    },

    
    focus: function() {
        var me = this,
            el = me.input;

        if (el && el.dom.focus) {
            el.dom.focus();
        }
        return me;
    },

    
    blur: function() {
        var me = this,
            el = this.input;

        if (el && el.dom.blur) {
            el.dom.blur();
        }
        return me;
    },

    onClick: function() {
        this.fireAction('click', arguments);
    },

    onChange: function() {
        this.fireAction('change', arguments);
    },

    onFocus: function() {
        this.fireAction('focus', arguments, 'doFocus');
    },

    
    doFocus: function(e) {
        var me = this;

        if (me.mask) {
            if (me.maskCorrectionTimer) {
                clearTimeout(me.maskCorrectionTimer);
            }
            me.hideMask();
        }

        if (!me.getIsFocused()) {
            me.setIsFocused(true);
            me.setStartValue(me.getValue());
        }
    },

    onBlur: function() {
        this.fireAction('blur', arguments, 'doBlur');
    },

    
    doBlur: function(e) {
        var me         = this,
            value      = me.getValue(),
            startValue = me.getStartValue();

        me.setIsFocused(false);

        if (String(value) != String(startValue)) {
            me.onChange(me, value, startValue);
        }

        me.showMask();

        
    },

    onKeyUp: function() {
        this.fireAction('keyup', arguments);
    },

    onPaste: function() {
        this.fireAction('paste', arguments);
    },

    onMouseDown: function() {
        this.fireAction('mousedown', arguments);
    }
});


Ext.define('Ext.field.Field', {
    extend: 'Ext.Component',
    alternateClassName: 'Ext.form.Field',
    alias : 'widget.field',
    requires: [
        'Ext.field.Input'
    ],

    
    isField: true,

    
    isFormField: true,

    config: {
        
        baseCls: Ext.baseCSSPrefix + 'field',

        
        label: null,

        
        labelAlign: null,

        
        labelWidth: '30%',

        
        component: null,

        

        
        clearIcon: null,

        
        required: false,

        

        
        inputType: null,

        
        name: null,

        
        value: null,

        
        tabIndex: null
    },

    cachedConfig: {
        
        labelCls: null,

        
        requiredCls: Ext.baseCSSPrefix + 'field-required'
    },

    
    constructor: function(config) {
        config = config || {};

        if (config.hasOwnProperty('useClearIcon')) {
            config.clearIcon = config.useClearIcon;
        }

        this.callParent([config]);
    },

    getElementConfig: function() {
        var prefix = Ext.baseCSSPrefix;

        return {
            reference: 'element',
            className: 'x-container',
            children: [
                {
                    reference: 'label',
                    cls: prefix + 'form-label',
                    children: [{
                        tag: 'span'
                    }]
                },
                {
                    reference: 'innerElement',
                    cls      : prefix + 'component-outer'
                }
            ]
        };
    },

    
    updateLabel: function(newLabel, oldLabel) {
        var renderElement = this.renderElement,
            prefix = Ext.baseCSSPrefix;

        if (newLabel) {
            this.label.down('span').update(newLabel);
            renderElement.addCls(prefix + 'field-labeled');
        } else {
            renderElement.removeCls(prefix + 'field-labeled');
        }
    },

    
    updateLabelAlign: function(newLabelAlign, oldLabelAlign) {
        var renderElement = this.renderElement,
            prefix = Ext.baseCSSPrefix;

        if (newLabelAlign) {
            renderElement.addCls(prefix + 'label-align-' + newLabelAlign);
        }

        if (oldLabelAlign) {
            renderElement.removeCls(prefix + 'label-align-' + oldLabelAlign);
        }
    },

    
    updateLabelCls: function(newLabelCls, oldLabelCls) {
        if (newLabelCls) {
            this.label.addCls(newLabelCls);
        }

        if (oldLabelCls) {
            this.label.removeCls(oldLabelCls);
        }
    },

    
    updateLabelWidth: function(newLabelWidth) {
        if (newLabelWidth) {
            this.label.setStyle('width', newLabelWidth);
        }
    },

    
    applyComponent: function(config) {
        return Ext.factory(config);
    },

    
    updateComponent: function(newComponent) {
        if (this.componentElement) {
            this.componentElement.destroy();
        }

        if (newComponent) {
            this.componentElement = this.innerElement.appendChild(newComponent.element);
        }
    },

    
    updateRequired: function(newRequired) {
        this.renderElement[newRequired ? 'addCls' : 'removeCls'](this.getRequiredCls());
    },

    
    updateRequiredCls: function(newRequiredCls, oldRequiredCls) {
        if (this.getRequired()) {
            this.renderElement.replaceCls(oldRequiredCls, newRequiredCls);
        }
    },

    
    initialize: function() {
        var me = this;
        me.callParent(arguments);

        me.doInitValue();
    },

    
    doInitValue: function() {
        
            this.originalValue = this.getValue();
    },

    
    reset: Ext.emptyFn,

    
    isDirty: function() {
        return false;
    }
}, function() {
    var prototype = this.prototype;

    this.override({
        constructor: function(config) {
            config = config || {};

            
            var deprecateProperty = function(property, obj, newProperty) {
                if (config.hasOwnProperty(property)) {
                    if (obj) {
                        config[obj] = config[obj] || {};
                        config[obj][(newProperty) ? newProperty : property] = config[obj][(newProperty) ? newProperty : property] || config[property];
                    } else {
                        config[newProperty] = config[property];
                    }

                    delete config[property];

                    Ext.Logger.deprecate("'" + property + "' config is deprecated, use the '" + ((obj) ? obj + "." : "") + ((newProperty) ? newProperty : property) + "' config instead", 2);
                }
            };

            
            
            deprecateProperty('inputCls', 'input', 'inputCls');
            
            
            deprecateProperty('fieldCls', 'input', 'inputCls');

            
            deprecateProperty('fieldLabel', null, 'label');

            if (config.hasOwnProperty('autoCreateField')) {
                Ext.Logger.deprecate("'autoCreateField' config is deprecated. If you are subclassing Ext.field.Field and you do not want a Ext.field.Input, set the 'input' config to false.", this);
            }

            this.callOverridden(arguments);
        }
    });

    prototype.__defineGetter__('fieldEl', function() {
        Ext.Logger.deprecate("'fieldEl' is deprecated, please use getInput() to get an instance of Ext.field.Field instead", this);

        return this.getInput().input;
    });

    prototype.__defineGetter__('labelEl', function() {
        Ext.Logger.deprecate("'labelEl' is deprecated", this);

        return this.getLabel().element;
    });
});


Ext.define('Ext.field.Checkbox', {
    extend: 'Ext.field.Field',
    alternateClassName: 'Ext.form.Checkbox',

    xtype: 'checkboxfield',

    isCheckbox: true,

    

    
    config: {
        
        ui: 'checkbox',

        
        value: '',

        
        checked: false,

        
        tabIndex: -1,

        
        component: {
            xtype   : 'input',
            type    : 'checkbox',
            useMask : true,
            inputCls: Ext.baseCSSPrefix + 'input-checkbox'
        }
    },

    
    initialize: function() {
        var me = this;

        me.callParent(arguments);

        me.getComponent().on({
            scope: me,
            masktap: 'onMaskTap'
        });
    },

    
    doInitValue: function() {
        var me = this,
            initialConfig = me.getInitialConfig();

        
        if (initialConfig.hasOwnProperty('value')) {
            me.originalState = initialConfig.value;
        }

        if (initialConfig.hasOwnProperty('checked')) {
            me.originalState = initialConfig.checked;
        }

        me.callParent(arguments);
    },

    
    updateInputType: function(newInputType) {
        var component = this.getComponent();
        if (component) {
            component.setType(newInputType);
        }
    },

    
    updateName: function(newName) {
        var component = this.getComponent();
        if (component) {
            component.setName(newName);
        }
    },

    
    getChecked: function() {
        var component = this.getComponent();

        
        var checked = component.getChecked();
        this._checked = checked;

        return this._checked;
    },

    getValue: function() {
        return this.getChecked();
    },

    setValue: function(value) {
        return this.setChecked(value);
    },

    setChecked: function(newChecked) {
        this.getChecked(); 
        this.updateChecked(newChecked);
        this._checked = newChecked;
    },

    updateChecked: function(newChecked) {
        var component = this.getComponent();
        component.setChecked(newChecked);
    },

    
    onMaskTap: function(component, e) {
        var me = this;

        if (me.getDisabled()) {
            return false;
        }

        
        component.input.dom.checked = !component.input.dom.checked;

        
        

        
        if (me.getChecked()) {
            me.fireAction('check', [me, e], 'doChecked');
        } else {
            me.fireAction('uncheck', [me, e], 'doUnChecked');
        }

        
        return false;
    },

    
    doChecked: Ext.emptyFn,

    
    doUnChecked: Ext.emptyFn,

    
    isChecked: function() {
        return this.getChecked();
    },

    
    check: function() {
        return this.setChecked(true);
    },

    
    uncheck: function() {
        return this.setChecked(false);
    },

    getSameGroupFields: function() {
        var component = this.up('formpanel') || this.up('fieldset');

        if (!component) {
            return null;
        }

        return component.query('[name=' + this.getName() + ']');
    },

    
    getGroupValues: function() {
        var values = [];

        this.getSameGroupFields().forEach(function(field) {
            if (field.getChecked()) {
                values.push(field.getValue());
            }
        });

        return values;
    },

    
    setGroupValues: function(values) {
        this.getSameGroupFields().forEach(function(field) {
            field.setChecked((values.indexOf(field.getValue()) !== -1));
        });

        return this;
    },

    
    resetGroupValues: function() {
        this.getSameGroupFields().forEach(function(field) {
            field.setChecked(field.originalState);
        });

        return this;
    },

    
    reset: function() {
        this.callParent(arguments);
        this.resetGroupValues();
    }
});


Ext.define('Ext.field.Hidden', {
    extend: 'Ext.field.Field',
    alternateClassName: 'Ext.form.Hidden',
    alias : 'widget.hiddenfield',

    config: {
        
        component: {
            xtype: 'input',
            type : 'hidden'
        },

        
        ui: 'hidden',

        
        hidden: true,

        
        tabIndex: -1
    }

});


Ext.define('Ext.field.Radio', {
    extend: 'Ext.field.Checkbox',
    alias : 'widget.radiofield',
    alternateClassName: 'Ext.form.Radio',

    isRadio: true,

    config: {
        
        ui: 'radio',

        
        component: {
            type: 'radio',
            inputCls: Ext.baseCSSPrefix + 'input-radio'
        }
    },

    getValue: function() {
        return this._value;
    },

    setValue: function(value) {
        this._value = value;
        return this;
    },

    
    getGroupValue: function() {
        var fields = this.getSameGroupFields(),
            ln = fields.length,
            i = 0,
            field;

        for (; i < ln; i++) {
            field = fields[i];
            if (field.getChecked()) {
                return field.getValue();
            }
        }

        return null;
    },

    
    setGroupValue: function(value) {
        var fields = this.getSameGroupFields(),
            ln = fields.length,
            i = 0,
            field;

        for (; i < ln; i++) {
            field = fields[i];
            if (field.getValue() === value) {
                field.setChecked(true);
                return field;
            }
        }
    }
});

Ext.define('Ext.field.Text', {
    extend: 'Ext.field.Field',
    alias : 'widget.textfield',
    alternateClassName: 'Ext.form.Text',

    

    

    

    

    

    config: {
        
        ui: 'text',

        
        clearIcon: true,

        
        placeHolder: null,

        
        maxLength: null,

        
        autoComplete: null,

        
        autoCapitalize: null,

        
        autoCorrect: null,

        
        readOnly: null,

        
        component: {
            xtype: 'input',
            type : 'text'
        }
    },

    
    initialize: function() {
        var me = this;

        me.callParent(arguments);

        me.getComponent().on({
            scope: this,

            keyup       : 'onKeyUp',
            change      : 'onChange',
            focus       : 'onFocus',
            blur        : 'onBlur',
            paste       : 'onPaste',
            mousedown   : 'onMouseDown',
            clearicontap: 'onClearIconTap'
        });
    },

    
    updateValue: function(newValue) {
        var component = this.getComponent();
        if (component) {
            component.setValue(newValue);
        }

        this[newValue ? 'showClearIcon' : 'hideClearIcon']();
    },

    getValue: function() {
        var me = this;
        me._value = me.getComponent().getValue();
        return me._value;
    },

    
    updatePlaceHolder: function(newPlaceHolder) {
        this.getComponent().setPlaceHolder(newPlaceHolder);
    },

    
    updateMaxLength: function(newMaxLength) {
        this.getComponent().setMaxLength(newMaxLength);
    },

    
    updateAutoComplete: function(newAutoComplete) {
        this.getComponent().setAutoComplete(newAutoComplete);
    },

    
    updateAutoCapitalize: function(newAutoCapitalize) {
        this.getComponent().setAutoCapitalize(newAutoCapitalize);
    },

    
    updateAutoCorrect: function(newAutoCorrect) {
        this.getComponent().setAutoCorrect(newAutoCorrect);
    },

    
    updateReadOnly: function(newReadOnly) {
        this.getComponent().setReadOnly(newReadOnly);
    },

    
    updateInputType: function(newInputType) {
        var component = this.getComponent();
        if (component) {
            component.setType(newInputType);
        }
    },

    
    updateName: function(newName) {
        var component = this.getComponent();
        if (component) {
            component.setName(newName);
        }
    },

    
    updateTabIndex: function(newTabIndex) {
        var component = this.getComponent();
        if (component) {
            component.setTabIndex(newTabIndex);
        }
    },

    
    doSetDisabled: function(disabled) {
        this.callParent(arguments);

        var component = this.getComponent();
        if (component) {
            component.setDisabled(disabled);
        }

        if (disabled) {
            this.hideClearIcon();
        } else {
            this.showClearIcon();
        }
    },

    
    doClearIconTap: function() {
        this.setValue('');
    },

    
    showClearIcon: function() {
        var me = this,
            clearIcon = this.getClearIcon();

        if (!me.getDisabled() && me.getValue() && clearIcon) {
            this.element.addCls(Ext.baseCSSPrefix + 'field-clearable');
        }

        return this;
    },

    
    hideClearIcon: function() {
        var clearIcon = this.getClearIcon();

        if (clearIcon) {
            this.element.removeCls(Ext.baseCSSPrefix + 'field-clearable');
        }
    },

    onChange: function(e) {
        this.fireAction('change', [this, e], 'doChange');
    },

    doChange: Ext.emptyFn,

    onKeyUp: function(e) {
        this.fireAction('keyup', [this, e], 'doKeyUp');
    },

    onFocus: function(e) {
        this.fireAction('focus', [this, e]);
    },

    onBlur: function(e) {
        this.fireAction('blur', [this, e]);
    },

    onPaste: function(e) {
        this.fireAction('paste', [this, e]);
    },

    onMouseDown: function(e) {
        this.fireAction('mousedown', [this, e]);
    },

    onClearIconTap: function(e) {
        this.fireAction('clearicontap', [this, e], 'doClearIconTap');
    },

    
    doKeyUp: function(me, e) {
        
        var value = me.getValue();

        
        this[value ? 'showClearIcon' : 'hideClearIcon']();

        if (e.browserEvent.keyCode === 13) {
            me.fireAction('action', [me, e], 'doAction');
        }
    },

    doAction: Ext.emptyFn,

    
    focus: function() {
        this.getComponent().focus();
        return this;
    },

    
    blur: function() {
        this.getComponent().blur();
        return this;
    },

    
    reset: function() {
        this.getComponent().reset();

        
        this.getValue();

        this[this._value ? 'showClearIcon' : 'hideClearIcon']();
    },

    
    isDirty: function() {
        var component = this.getComponent();
        if (component) {
            return component.isDirty();
        }
        return false;
    }
});




Ext.define('Ext.field.Email', {
    extend: 'Ext.field.Text',
    alternateClassName: 'Ext.form.Email',
    alias : 'widget.emailfield',

    config: {
        
        component: {
	        type: 'email'
	    },

        
        autoCapitalize: false
    }
});







Ext.define('Ext.field.Number', {
    extend: 'Ext.field.Text',
    alias : 'widget.numberfield',
    alternateClassName: 'Ext.form.Number',

    config: {
        
        component: {
            type: 'number'
        },

        
        ui: 'number',

        
        minValue: null,

        
        maxValue: null,

        
        stepValue: null
    },

    applyValue: function(value) {
        var minValue = this.getMinValue(),
            maxValue = this.getMaxValue();
        
        if (Ext.isNumber(minValue)) {
            value = Math.max(value, minValue);
        }

        if (Ext.isNumber(maxValue)) {
            value = Math.min(value, maxValue);
        }

        return parseFloat(value);
    },

    getValue: function() {
        var value = this.callParent();
        return parseFloat(value || 0);
    },

    
    updateMinValue: function(newMinValue) {
        this.getComponent().setMinValue(newMinValue);
    },

    
    updateMaxValue: function(newMaxValue) {
        this.getComponent().setMaxValue(newMaxValue);
    },

    
    updateStepValue: function(newStepValue) {
        this.getComponent().setStepValue(newStepValue);
    }
});


Ext.define('Ext.field.Password', {
    extend: 'Ext.field.Text',
    alias : 'widget.passwordfield',
    alternateClassName: 'Ext.form.Password',

    config: {
        
        autoCapitalize: false,

        
        component: {
	        type: 'password'
	    }
    }
});


Ext.define('Ext.field.Search', {
    extend: 'Ext.field.Text',
    alias : 'widget.searchfield',
    alternateClassName: 'Ext.form.Search',

    config: {
        
        component: {
	        type: 'search'
	    },

	    
	    ui: 'search'
    }
});


Ext.define('Ext.field.Spinner', {
    extend: 'Ext.field.Number',
    alias : 'widget.spinnerfield',
    alternateClassName: 'Ext.form.Spinner',
    requires: ['Ext.util.TapRepeater'],

    
    
    

    config: {
        
        cls: Ext.baseCSSPrefix + 'spinner',

        
        minValue: Number.NEGATIVE_INFINITY,
        
        maxValue: Number.MAX_VALUE,

        
        increment: 1,

        
        accelerateOnTapHold: true,

        
        cycle: false,

        
        clearIcon: false,

        defaultValue: 0,

        
        tabIndex: -1
    },

    constructor: function() {
        this.callParent(arguments);

        if (!this.getValue()) {
            this.setValue(this.getDefaultValue());
        }
    },

    
    updateComponent: function(newComponent) {
        this.callParent(arguments);

        var cls = this.getCls();

        if (newComponent) {
            this.spinDownButton = newComponent.element.createChild({
                cls : cls + '-button ' + cls + '-button-down',
                html: '-'
            });

            newComponent.element.insertFirst(this.spinDownButton);

            this.spinUpButton = newComponent.element.createChild({
                cls : cls + '-button ' + cls + '-button-up',
                html: '+'
            });

            this.downRepeater = this.createRepeater(this.spinDownButton, this.onSpinDown);
            this.upRepeater = this.createRepeater(this.spinUpButton,     this.onSpinUp);
        }
    },

    
    applyValue: function(value) {
        value = parseFloat(value);
        if (isNaN(value)) {
            value = this.getDefaultValue();
        }
        return this.callParent([value]);
    },

    
    createRepeater: function(el, fn) {
        var me = this,
            repeater = Ext.create('Ext.util.TapRepeater', {
                el: el,
                accelerate: me.getAccelerateOnTapHold()
            });

        repeater.on({
            tap: fn,
            touchstart: 'onTouchStart',
            touchend: 'onTouchEnd',
            scope: me
        });

        return repeater;
    },

    
    onSpinDown: function() {
        if (!this.getDisabled()) {
            this.spin(true);
        }
    },

    
    onSpinUp: function() {
        if (!this.getDisabled()) {
            this.spin(false);
        }
    },

    
    onTouchStart: function(repeater) {
        if (!this.getDisabled()) {
            repeater.getEl().addCls(Ext.baseCSSPrefix + 'button-pressed');
        }
    },

    
    onTouchEnd: function(repeater) {
        repeater.getEl().removeCls(Ext.baseCSSPrefix + 'button-pressed');
    },

    
    spin: function(down) {
        var me = this,
            value = parseInt(me.getValue(), 10),
            increment = me.getIncrement(),
            direction = down ? 'down' : 'up';

        if (down) {
            value -= increment;
        }
        else {
            value += increment;
        }

        me.setValue(value);
        value = me._value;

        me.fireAction('spin', [me, value, direction], 'doSpin');
        me.fireAction('spin' + direction, [me, value], 'doSpin' + Ext.String.capitalize(direction));
    },

    doSpin: Ext.emptyFn,
    doSpinUp: Ext.emptyFn,
    doSpinDown: Ext.emptyFn,

    reset: function() {
        this.setValue(this.getDefaultValue());
    },

    
    destroy: function() {
        var me = this;
        Ext.destroy(me.downRepeater, me.upRepeater);
        me.callParent(arguments);
    }
}, function() {
    
});


Ext.define('Ext.field.TextAreaInput', {
    extend: 'Ext.field.Input',
    xtype : 'textareainput',

    config: {
        
        tag: 'textarea'
    },

    
    getTemplate: function() {
        var items = [
            {
                reference: 'input',
                tag: this.getTag()
            },
            {
                reference: 'clearIcon',
                cls: 'x-clear-icon',
                html: 'x'
            }
        ];

        items.push({
            reference: 'mask',
            classList: [this.getMaskCls()]
        });

        return items;
    }
});


Ext.define('Ext.field.TextArea', {
    extend: 'Ext.field.Text',
    alias : 'widget.textareafield',
    requires: ['Ext.field.TextAreaInput'],
    alternateClassName: 'Ext.form.TextArea',

    config: {
        
        ui: 'textarea',

        
        autoCapitalize: false,

        
        component: {
            xtype: 'textareainput'
        },

        
        maxRows: null
    },

    
    updateMaxRows: function(newRows) {
        this.getComponent().setMaxRows(newRows);
    }
});


Ext.define('Ext.field.Url', {
    extend: 'Ext.field.Text',
    alias : 'widget.urlfield',
    alternateClassName: 'Ext.form.Url',

    config: {
        
        autoCapitalize: false,

        
        component: {
	        type: 'url'
	    }
    }
});


Ext.define('Ext.scroll.indicator.Abstract', {
    extend: 'Ext.Component',

    config: {
        baseCls: 'x-scroll-indicator',

        
        axis: 'x',

        value: 0,

        length: null,

        hidden: true
    },

    cachedConfig: {
        ratio: 1,

        barCls: 'x-scroll-bar'
    },

    barElement: null,

    barLength: 0,

    gapLength: 0,

    getElementConfig: function() {
        return {
            reference: 'barElement',
            children: [this.callParent()]
        };
    },

    applyRatio: function(ratio) {
        if (isNaN(ratio)) {
            ratio = 1;
        }

        return ratio;
    },

    refresh: function() {
        var bar = this.barElement,
            barDom = bar.dom,
            ratio = this.getRatio(),
            axis = this.getAxis(),
            barLength = (axis === 'x') ? barDom.offsetWidth : barDom.offsetHeight,
            length = barLength * ratio;

        this.barLength = barLength;

        this.gapLength = barLength - length;

        this.setLength(length);

        this.updateValue(this.getValue());
    },

    updateBarCls: function(barCls) {
        this.barElement.addCls(barCls);
    },

    updateAxis: function(axis) {
        this.element.addCls(this.getBaseCls(), null, axis);
        this.barElement.addCls(this.getBarCls(), null, axis);
    },

    updateValue: function(value) {
        this.setOffset(this.gapLength * value);
    },

    doSetHidden: function(hidden) {
        var elementDomStyle = this.element.dom.style;

        if (hidden) {
            elementDomStyle.opacity = '0';
        }
        else {
            elementDomStyle.opacity = '';
        }
    },

    updateLength: function(length) {
        var axis = this.getAxis();

        if (axis === 'x') {
            this.element.setWidth(length);
        }
        else {
            this.element.setHeight(length);
        }
    },

    setOffset: function(offset) {
        var axis = this.getAxis(),
            element = this.element;

        if (axis === 'x') {
            element.setLeft(offset);
        }
        else {
            element.setTop(offset);
        }
    }
});


Ext.define('Ext.scroll.indicator.CssTransform', {
    extend: 'Ext.scroll.indicator.Abstract',

    config: {
        ui: 'csstransform'
    },

    getElementConfig: function() {
        var config = this.callParent();

        config.children[0].children = [
            {
                reference: 'startElement'
            },
            {
                reference: 'middleElement'
            },
            {
                reference: 'endElement'
            }
        ];

        return config;
    },

    refresh: function() {
        var axis = this.getAxis(),
            startElementDom = this.startElement.dom,
            endElementDom = this.endElement.dom,
            middleElement = this.middleElement,
            startElementLength, endElementLength;

        if (axis === 'x') {
            startElementLength = startElementDom.offsetWidth;
            endElementLength = endElementDom.offsetWidth;
            middleElement.setLeft(startElementLength);
        }
        else {
            startElementLength = startElementDom.offsetHeight;
            endElementLength = endElementDom.offsetHeight;
            middleElement.setTop(startElementLength);
        }

        this.startElementLength = startElementLength;
        this.endElementLength = endElementLength;
        this.minLength = startElementLength + endElementLength;

        this.callParent();
    },

    applyLength: function(length) {
        return Math.round(Math.max(this.minLength, length));
    },

    updateLength: function(length) {
        var axis = this.getAxis(),
            endElementStyle = this.endElement.dom.style,
            middleElementStyle = this.middleElement.dom.style,
            endElementLength = this.endElementLength,
            endElementOffset = length - endElementLength,
            middleElementLength = endElementOffset - this.startElementLength;

        if (axis === 'x') {
            endElementStyle.webkitTransform = 'translate3d(' + endElementOffset + 'px, 0, 0)';
            middleElementStyle.webkitTransform = 'translate3d(0, 0, 0) scaleX(' + middleElementLength + ')';
        }
        else {
            endElementStyle.webkitTransform = 'translate3d(0, ' + endElementOffset + 'px, 0)';
            middleElementStyle.webkitTransform = 'translate3d(0, 0, 0) scaleY(' + middleElementLength + ')';
        }
    },

    updateValue: function(value) {
        var barLength = this.barLength,
            gapLength = this.gapLength,
            length = this.getLength(),
            newLength, offset, extra;

        if (value < 0) {
            offset = 0;
            this.updateLength(this.applyLength(length + value * barLength));
        }
        else if (value > 1) {
            extra = Math.round((value - 1) * barLength);
            newLength = this.applyLength(length - extra);
            extra = length - newLength;
            this.updateLength(newLength);
            offset = gapLength + extra;
        }
        else {
            offset = gapLength * value;
        }

        this.setOffset(offset);
    },

    setOffset: function(offset) {
        var axis = this.getAxis(),
            elementStyle = this.element.dom.style;

        offset = Math.round(offset);

        if (axis === 'x') {
            elementStyle.webkitTransform = 'translate3d(' + offset + 'px, 0, 0)';
        }
        else {
            elementStyle.webkitTransform = 'translate3d(0, ' + offset + 'px, 0)';
        }
    }
});


Ext.define('Ext.scroll.indicator.ScrollPosition', {
    extend: 'Ext.scroll.indicator.Abstract',

    config: {
        ui: 'scrollposition'
    },

    getElementConfig: function() {
        var config = this.callParent(arguments);

        config.children.unshift({
            className: 'x-scroll-bar-stretcher'
        });

        return config;
    },

    updateValue: function(value) {
        if (this.gapLength === 0) {
            if (value > 1) {
                value = value - 1;
            }

            this.setOffset(this.barLength * value);
        }
        else {
            this.setOffset(this.gapLength * value);
        }
    },

    setLength: function(length) {
        var axis = this.getAxis(),
            scrollOffset = this.barLength,
            barDom = this.barElement.dom,
            element = this.element;

        this.callParent(arguments);

        if (axis === 'x') {
            barDom.scrollLeft = scrollOffset;
            element.setLeft(scrollOffset);
        }
        else {
            barDom.scrollTop = scrollOffset;
            element.setTop(scrollOffset);
        }
    },

    setOffset: function(offset) {
        var axis = this.getAxis(),
            scrollOffset = this.barLength,
            barDom = this.barElement.dom;

        offset = scrollOffset - offset;

        if (axis === 'x') {
            barDom.scrollLeft = offset;
        }
        else {
            barDom.scrollTop = offset;
        }
    }
});


Ext.define('Ext.scroll.Indicator', {
    requires: [
        'Ext.scroll.indicator.ScrollPosition',
        'Ext.scroll.indicator.CssTransform'
    ],
    
    alternateClassName: 'Ext.util.Indicator',

    constructor: function(config) {
        if (Ext.os.is.Android2) {
            return new Ext.scroll.indicator.ScrollPosition(config);
        }
        else {
            return new Ext.scroll.indicator.CssTransform(config);
        }
    }
});


Ext.define('Ext.scroll.View', {
    extend: 'Ext.EventedBase',

    alternateClassName: 'Ext.util.ScrollView',

    requires: [
        'Ext.scroll.Scroller',
        'Ext.scroll.Indicator'
    ],

    config: {
        element: null,
        scroller: {},
        indicators: {
            x: {
                axis: 'x'
            },
            y: {
                axis: 'y'
            }
        },
        cls: Ext.baseCSSPrefix + 'scroll-view'
    },

    processConfig: function(config) {
        if (!config) {
            return null;
        }

        if (typeof config == 'string') {
            config = {
                direction: config
            }
        }

        config = Ext.merge({}, config);

        var scrollerConfig = config.scroller,
            name;

        if (!scrollerConfig) {
            config.scroller = scrollerConfig = {};
        }

        for (name in config) {
            if (config.hasOwnProperty(name)) {
                if (!this.hasConfig(name)) {
                    scrollerConfig[name] = config[name];
                    delete config[name];
                }
            }
        }

        return config;
    },

    constructor: function(config) {
        config = this.processConfig(config);

        this.indicatorLength = { x: 0, y: 0 };

        this.indicatorMaxLength = { x: 0, y: 0 };

        this.indicatorMaxOffset = { x: 0, y: 0 };

        this.useIndicators = { x: true, y: true };

        this.initConfig(config);
    },

    setConfig: function(config) {
        return this.callParent([this.processConfig(config)]);
    },

    applyScroller: function(config, currentScroller) {
        return Ext.factory(config, Ext.scroll.Scroller, currentScroller);
    },

    applyIndicators: function(config, indicators) {
        var defaultClass = Ext.scroll.Indicator,
            useIndicators = this.useIndicators;

        if (!config) {
            config = {};
        }

        if (!config.x) {
            useIndicators.x = false;
            config.x = {};
        }

        if (!config.y) {
            useIndicators.y = false;
            config.y = {};
        }

        return {
            x: Ext.factory(config.x, defaultClass, indicators && indicators.x),
            y: Ext.factory(config.y, defaultClass, indicators && indicators.y)
        };
    },

    updateIndicators: function(indicators) {
        this.indicatorsGrid = Ext.Element.create({
            className: 'x-scroll-bar-grid-wrapper',
            children: [{
                className: 'x-scroll-bar-grid',
                children: [
                    {
                        children: [{}, {
                            children: [indicators.y.barElement]
                        }]
                    },
                    {
                        children: [{
                            children: [indicators.x.barElement]
                        }, {}]
                    }
                ]
            }]
        });
    },

    updateScroller: function(scroller) {
        scroller.on({
            scope: this,
            scrollstart: 'onScrollStart',
            scroll     : 'onScroll',
            scrollend  : 'onScrollEnd',
            refresh    : 'refreshIndicators'
        });
    },
    
    isAxisEnabled: function(axis) {
        return this.getScroller().isAxisEnabled(axis) && this.useIndicators[axis];
    },

    applyElement: function(element) {
        if (element) {
            return Ext.get(element);
        }
    },

    updateElement: function(element) {
        var scrollerElement = element.getFirstChild().getFirstChild(),
            scroller = this.getScroller();

        element.addCls(this.getCls());
        element.insertFirst(this.indicatorsGrid);

        scroller.setElement(scrollerElement);

        this.refreshIndicators();

        return this;
    },

    getSize: function() {
        var dom = this.getElement().dom;

        return {
            x: dom.offsetWidth,
            y: dom.offsetHeight
        };
    },

    showIndicator: function(axis) {
        if (this.isAxisEnabled(axis)) {
            this.getIndicators()[axis].show();
        }
    },

    hideIndicator: function(axis) {
        if (this.isAxisEnabled(axis)) {
            this.getIndicators()[axis].hide();
        }
    },

    onScrollStart: function() {
        this.showIndicator('x');
        this.showIndicator('y');
    },

    onScroll: function(scroller, x, y) {
        this.setIndicatorValue('x', x);
        this.setIndicatorValue('y', y);
    },

    setIndicatorValue: function(axis, scrollerPosition) {
        if (!this.isAxisEnabled(axis)) {
            return this;
        }

        var scroller = this.getScroller(),
            scrollerMaxPosition = scroller.getMaxPosition()[axis],
            scrollerContainerSize, value;

        if (scrollerMaxPosition === 0) {
            scrollerContainerSize = scroller.getContainerSize()[axis];

            if (scrollerPosition < 0) {
                value = scrollerPosition / scrollerContainerSize;
            }
            else {
                value = 1 + (scrollerPosition / scrollerContainerSize);
            }
        }
        else {
            value = scrollerPosition / scrollerMaxPosition;
        }

        this.getIndicators()[axis].setValue(value);
    },

    onScrollEnd: function() {
        this.hideIndicator('x');
        this.hideIndicator('y');
    },

    refreshIndicator: function(axis) {
        if (!this.isAxisEnabled(axis)) {
            return this;
        }

        var scroller = this.getScroller(),
            indicator = this.getIndicators()[axis],
            scrollerContainerSize = scroller.getContainerSize()[axis],
            scrollerSize = scroller.getSize()[axis],
            ratio = scrollerContainerSize / scrollerSize;

        indicator.setRatio(ratio);
        indicator.refresh();
    },

    refresh: function() {
        return this.getScroller().refresh();
    },

    refreshIndicators: function() {
        this.refreshIndicator('x');
        this.refreshIndicator('y');
    },

    destroy: function() {
        var element = this.getElement(),
            indicators = this.getIndicators();

        if (element) {
            element.removeCls(this.getCls());
        }

        indicators.x.destroy();
        indicators.y.destroy();

        this.getScroller().destroy();

        this.callParent(arguments);
    }
});

Ext.define('Ext.behavior.Scrollable', {

    extend: 'Ext.behavior.Behavior',

    requires: [
        'Ext.scroll.View'
    ],

    constructor: function() {
        this.listeners = {
            painted: 'onComponentPainted',
            scope: this
        };

        this.callParent(arguments);
    },

    onComponentPainted: function() {
        this.scrollView.refresh();
    },

    setConfig: function(config) {
        var scrollView = this.scrollView,
            component = this.component,
            scrollViewElement, scrollContainer, scrollerElement;

        if (config) {
            if (!scrollView) {
                this.scrollView = scrollView = new Ext.scroll.View(config);
                scrollView.on('destroy', 'onScrollViewDestroy', this);

                component.setUseBodyElement(true);

                this.scrollerElement = scrollerElement = component.innerElement;
                this.scrollContainer = scrollContainer = scrollerElement.wrap();
                this.scrollViewElement = scrollViewElement = component.bodyElement;

                scrollView.setElement(scrollViewElement);

                if (component.isPainted()) {
                    this.onComponentPainted(component);
                }

                component.on(this.listeners);
            }
            else if (Ext.isObject(config)) {
                scrollView.setConfig(config);
            }
        }
        else if (scrollView) {
            scrollView.destroy();
        }
    },

    getScrollView: function() {
        return this.scrollView;
    },

    onScrollViewDestroy: function() {
        var component = this.component;

        this.scrollerElement.unwrap();
        this.scrollContainer.destroy();

        component.un(this.listeners);

        delete this.scrollerElement;
        delete this.scrollView;
        delete this.scrollContainer;
    },

    onComponentDestroy: function() {
        var scrollView = this.scrollView;

        if (scrollView) {
            scrollView.destroy();
        }
    }
});


Ext.define('Ext.Container', {
    extend: 'Ext.Component',

    alternateClassName: 'Ext.lib.Container',

    requires: [
        'Ext.layout.Layout',
        'Ext.ItemCollection',
        'Ext.behavior.Scrollable',
        'Ext.Mask'
    ],

    xtype: 'container',

    eventedConfig: {
        
        activeItem: 0
    },

    config: {
        
        layout: null,

        
        defaults: null,

        
        items: null,

        
        autoDestroy: true,

        
        defaultType: null,

        
        scrollable: null,

        //@private

        useBodyElement: null
    },

    isContainer: true,

    delegateListeners: {
        delegate: '> component',
        centeredchange: 'onItemCenteredChange',
        dockedchange: 'onItemDockedChange',
        floatingchange: 'onItemFloatingChange'
    },

    constructor: function(config) {
        var me = this;

        me._items = me.items = new Ext.ItemCollection();
        me.innerItems = [];

        
        
        me.$onAdd = me.onAdd;
        me.onAdd = me.onFirstAdd;

        me.callParent(arguments);
    },

    getElementConfig: function() {
        return {
            reference: 'element',
            className: 'x-container',
            children: [{
                reference: 'innerElement',
                className: 'x-inner'
            }]
        };
    },

    updateBaseCls: function(newBaseCls, oldBaseCls) {
        var me = this,
            ui = me.getUi();

        if (newBaseCls) {
            this.addCls(newBaseCls);
            this.innerElement.addCls(newBaseCls, null, 'inner');

            if (ui) {
                this.addCls(newBaseCls, null, ui);
            }
        }

        if (oldBaseCls) {
            this.removeCls(oldBaseCls);
            this.innerElement.removeCls(newBaseCls, null, 'inner');

            if (ui) {
                this.removeCls(oldBaseCls, null, ui);
            }
        }
    },

    updateUseBodyElement: function(useBodyElement) {
        if (useBodyElement) {
            this.bodyElement = this.innerElement.wrap({
                cls: 'x-body'
            });
        }
    },

    applyItems: function(items) {
        if (items) {
            this.getDefaultType();
            this.getDefaults();

            this.add(items);
        }
    },

    
    onFirstAdd: function() {
        var onAdd = this.onAdd = this.$onAdd;

        delete this.$onAdd;

        this.setLayout(new Ext.layout.Layout(this, this.getLayout() || 'default'));

        if (this.innerHtmlElement && !this.getHtml()) {
            this.innerHtmlElement.destroy();
            delete this.innerHtmlElement;
        }

        this.on(this.delegateListeners);

        return onAdd.apply(this, arguments);
    },

    updateDefaultType: function(defaultType) {
        
        this.defaultItemClass = Ext.ClassManager.getByAlias('widget.' + defaultType);

        if (!this.defaultItemClass) {
            Ext.Logger.error("Invalid defaultType of: '" + defaultType + "', must be a valid component xtype");
        }
    },

    applyDefaults: function(defaults) {
        if (defaults) {
            this.factoryItem = this.factoryItemWithDefaults;
            return defaults;
        }
    },

    factoryItem: function(item) {
        if (!item) {
            Ext.Logger.error("Invalid item given: " + item + ", must be either the config object to factory a new item, " +
                "or an existing component instance");
        }

        return Ext.factory(item, this.defaultItemClass);
    },

    factoryItemWithDefaults: function(item) {
        if (!item) {
            Ext.Logger.error("Invalid item given: " + item + ", must be either the config object to factory a new item, " +
                "or an existing component instance");
        }

        var me = this,
            defaults = me.getDefaults(),
            instance;

        if (!defaults) {
            return Ext.factory(item, me.defaultItemClass);
        }

        
        if (item.isComponent) {
            instance = item;

            
            if (defaults && item.isInnerItem() && !me.has(instance)) {
                instance.setConfig(defaults, true);
            }
        }
        
        else {
            if (defaults && !item.ignoreDefaults) {
                
                
                
                if (!(
                        item.hasOwnProperty('left') &&
                        item.hasOwnProperty('right') &&
                        item.hasOwnProperty('top') &&
                        item.hasOwnProperty('bottom') &&
                        item.hasOwnProperty('docked') &&
                        item.hasOwnProperty('centered')
                    )) {
                    item = Ext.mergeIf({}, item, defaults);
                }
            }

            instance = Ext.factory(item, me.defaultItemClass);
        }

        return instance;
    },

    
    add: function(newItems) {
        var me = this,
            i, ln, item;

        newItems = Ext.Array.from(newItems);

        ln = newItems.length;

        for (i = 0; i < ln; i++) {
            item = me.factoryItem(newItems[i]);

            this.doAdd(item);
        }

        return item;
    },

    
    doAdd: function(item) {
        var me = this,
            items = me.getItems(),
            index;

        if (!items.has(item)) {
            index = items.length;
            items.add(item);

            if (item.isInnerItem()) {
                me.insertInner(item);
            }

            item.setParent(me);

            me.onAdd(item, index);
        }
    },

    
    remove: function(item, destroy) {
        var me = this,
            items = me.items,
            index = me.indexOf(item);

        if (destroy === undefined) {
            destroy = me.getAutoDestroy();
        }

        if (index !== -1) {
            me.onRemove(item, index);
            item.setParent(null);
            items.remove(item);

            if (item.isInnerItem()) {
                me.removeInner(item);
            }

            if (destroy) {
                item.destroy();
            }
        }

        return me;
    },

    
    removeAll: function(destroy) {
        var me = this,
            items = me.items,
            item;

        if (destroy === undefined) {
            destroy = me.getAutoDestroy();
        }

        me.innerItems.length = 0;

        while (items.length > 0) {
            item = items.getAt(0);
            items.removeAt(0);

            me.onRemove(item, 0);

            item.setParent(null);

            if (destroy) {
                item.destroy();
            }
        }

        return me;
    },

    
    getAt: function(index) {
        return this.items.getAt(index);
    },


    
    removeAt: function(index) {
        var item = this.getAt(index);

        if (item) {
            this.remove(item);
        }

        return this;
    },

    
    has: function(item) {
        return this.getItems().indexOf(item) != -1;
    },

    
    hasInnerItem: function(item) {
        return this.innerItems.indexOf(item) != -1;
    },

    
    indexOf: function(item) {
        
        return this.getItems().indexOf(item);
    },

    
    insertInner: function(item, index) {
        var me = this,
            items = me.getItems().items,
            innerItems = me.innerItems,
            nextSibling;

        if (typeof index == 'number') {
            do {
                nextSibling = items[++index];
            } while (nextSibling && !nextSibling.isInnerItem());

            if (!nextSibling) {
                innerItems.push(item);
            }
            else {
                innerItems.splice(innerItems.indexOf(nextSibling), 0, item);
            }
        }
        else {
            innerItems.push(item);
        }

        return me;
    },

    
    removeInner: function(item) {
        Ext.Array.remove(this.innerItems, item);

        return this;
    },

    
    insert: function(index, item) {
        var me = this,
            i;

        if (Ext.isArray(item)) {
            for (i = item.length - 1; i >= 0; i--) {
                me.insert(index, item[i]);
            }

            return me;
        }

        item = this.factoryItem(item);

        this.doInsert(index, item);

        return item;
    },

    
    doInsert: function(index, item) {
        var me = this,
            items = me.items,
            itemsLength = items.length,
            currentIndex, isInnerItem;

        isInnerItem = item.isInnerItem();

        if (index > itemsLength) {
            index = itemsLength;
        }

        if (items[index - 1] === item) {
            return me;
        }

        currentIndex = me.indexOf(item);

        if (currentIndex !== -1) {
            if (currentIndex < index) {
                index -= 1;
            }
            items.removeAt(currentIndex);
            if (isInnerItem) {
                me.removeInner(item);
            }
        }
        else {
            item.setParent(me);
        }

        items.insert(index, item);

        if (isInnerItem) {
            me.insertInner(item, index);
        }
        if (currentIndex !== -1) {
            me.onMove(item, index, currentIndex);
        }
        else {
            me.onAdd(item, index);
        }
    },

    
    insertFirst: function(item) {
        return this.insert(0, item);
    },

    
    insertLast: function(item) {
        return this.insert(this.getItems().length, item);
    },

    
    insertBefore: function(item, relativeToItem) {
        var index = this.indexOf(relativeToItem);

        if (index !== -1) {
            this.insert(index, item);
        }
        return this;
    },

    
    insertAfter: function(item, relativeToItem) {
        var index = this.indexOf(relativeToItem);

        if (index !== -1) {
            this.insert(index + 1, item);
        }
        return this;
    },

    
    onAdd: function(item, index) {
        this.getLayout().onItemAdd(item, index);

        if (this.initialized && item.isInnerItem() && !this.getActiveItem()) {
            this.setActiveItem(item);
        }

        if (this.initialized) {
            this.fireEvent('add', this, item, index);
        }

        if (this.isRendered() && item.setRendered(true)) {
            item.fireEvent('renderedchange', item, true);
        }
    },

    
    onRemove: function(item, index) {
        this.getLayout().onItemRemove(item, index);

        if (item === this.getActiveItem()) {
            this.setActiveItem(0);
        }

        this.fireEvent('remove', this, item, index);

        if (this.isRendered() && item.setRendered(false)) {
            item.fireEvent('renderedchange', item, false);
        }
    },

    
    onMove: function(item, toIndex, fromIndex) {
        if (item.isDocked()) {
            item.setDocked(null);
        }

        this.getLayout().onItemMove(item, toIndex, fromIndex);

        this.fireEvent('move', this, item, toIndex, fromIndex);
    },

    
    onItemCenteredChange: function(item, centered) {
        if (!item.isFloating() && !item.isDocked()) {
            if (centered) {
                this.removeInner(item);
            }
            else {
                this.insertInner(item, this.indexOf(item));
            }
        }

        this.getLayout().onItemCenteredChange(item, centered);
    },

    
    onItemFloatingChange: function(item, floating) {
        if (!item.isCentered() && !item.isDocked()) {
            if (floating) {
                this.removeInner(item);
            }
            else {
                this.insertInner(item, this.indexOf(item));
            }
        }

        this.getLayout().onItemFloatingChange(item, floating);
    },

    
    onItemDockedChange: function(item, docked, oldDocked) {
        if (!item.isCentered() && !item.isFloating()) {
            if (docked) {
                this.removeInner(item);
            }
            else {
                this.insertInner(item, this.indexOf(item));
            }
        }

        this.getLayout().onItemDockedChange(item, docked, oldDocked);
    },

    
    getInnerItems: function() {
        return this.innerItems;
    },

    
    applyActiveItem: function(item) {
        
        this.getItems();

        if (typeof item == 'number') {
            return this.getInnerItems()[item] || null;
        }
        else if (item) {
            if (!item.isComponent) {
                item = this.factoryItem(item);
            }

            if (!item.isInnerItem()) {
                Ext.Logger.error("Setting activeItem to be a non-inner item");
            }

            return item;
        }
    },

    
    doSetActiveItem: function(newActiveItem, oldActiveItem) {
        if (newActiveItem) {
            newActiveItem.fireAction('activate');

            if (!this.has(newActiveItem)) {
                this.add(newActiveItem);
            }

            if (oldActiveItem) {
                oldActiveItem.fireAction('deactivate');
            }

            this.getLayout().onActiveItemChange(newActiveItem, oldActiveItem);
        }
    },

    
    setRendered: function(rendered) {
        if (this.callParent(arguments)) {
            var items = this.items.items,
                i, ln;

            for (i = 0,ln = items.length; i < ln; i++) {
                items[i].setRendered(rendered);
            }

            return true;
        }

        return false;
    },

    
    getScrollableBehavior: function() {
        var behavior = this.scrollableBehavior;

        if (!behavior) {
            behavior = this.scrollableBehavior = new Ext.behavior.Scrollable(this);
        }

        return behavior;
    },

    
    applyScrollable: function(config) {
        this.getScrollableBehavior().setConfig(config);
    },

    
    getScrollable: function() {
        return this.getScrollableBehavior().getScrollView();
    },

    
    
    
    
    
    getRefItems: function(deep) {
        var items = this.getItems().items.slice(),
            ln = items.length,
            i, item;

        if (deep) {
            for (i = 0; i < ln; i++) {
                item = items[i];

                if (item.getRefItems) {
                    items = items.concat(item.getRefItems(true));
                }
            }
        }

        return items;
    },

    
    getComponent: function(component) {
        if (Ext.isObject(component)) {
            component = component.getItemId();
        }

        return this.getItems().get(component);
    },

    
    query: function(selector) {
        return Ext.ComponentQuery.query(selector, this);
    },

    
    child: function(selector) {
        return this.query('> ' + selector)[0] || null;
    },

    
    down: function(selector) {
        return this.query(selector)[0] || null;
    },

    doSetHidden: function(hidden) {
        this.callParent(arguments);

        if (hidden || !this.isPainted()) {
            return;
        }

        var items = this.getItems().items,
            ln = items.length,
            item, i;

        for (i = 0; i < ln; i++) {
            item = items[i];
            if (!item.getHidden()) {
                item.doSetHidden(false);
            }
        }
    },

    onClassExtended: function(Class, members) {
    },

    destroy: function() {
        this.removeAll(true);
        this.callParent(arguments);
    }

}, function() {
    this.addMember('defaultItemClass', this);

    
    Ext.deprecateClassMethod(this, 'addAll', 'add');

    
    Ext.deprecateClassMethod(this, 'removeDocked', 'remove');

    
    this.override({
        constructor: function(config) {
            config = config || {};

            var dockedItems = config.dockedItems,
                i, ln, item;

            this.callParent(arguments);

            if (dockedItems) {
                Ext.Logger.deprecate("'dockedItems' config is deprecated, please add all docked items inside the 'items' config with a 'docked' property indicating the docking position instead, i.e { /*...*/ docked: 'top' /*...*/ }");

                dockedItems = Ext.Array.from(dockedItems);

                for (i = 0,ln = dockedItems.length; i < ln; i++) {
                    item = dockedItems[i];
                    if ('dock' in item) {
                        Ext.Logger.deprecate("'dock' config for docked items is deprecated, please use 'docked' instead");
                        item.docked = item.dock;
                    }
                }

                this.add(dockedItems);
            }
        },

        add: function() {
            var args = arguments;

            if (args.length > 1) {
                if (typeof args[0] == 'number') {
                    Ext.Logger.deprecate("add(index, item) method signature is deprecated, please use insert(index, item) instead");
                    return this.insert(args[0], args[1]);
                }
                Ext.Logger.deprecate("Passing items as multiple arguments is deprecated, please use one single array of items instead");
                args = [Array.prototype.slice.call(args)];
            }

            return this.callParent(args);
        },

        applyDefaults: function(defaults) {
            if (typeof defaults == 'function') {
                Ext.Logger.deprecate("Passing a function as 'defaults' is deprecated. To add custom logics when " +
                    "'defaults' is applied to each item, have your own factoryItem() method in your sub-class instead");
            }

            return this.callParent(arguments);
        },

        factoryItemWithDefaults: function(item) {
            var defaults = this.getDefaults(),
                customDefaults, ret;

            
            if (typeof defaults == 'function') {
                customDefaults = defaults.call(this, item);
            }

            
            if (typeof item == 'string') {
                Ext.Logger.deprecate("Passing a string id of item ('"+item+"') is deprecated, please pass a reference to that item instead");

                item = Ext.getCmp(item);
            }

            if (customDefaults) {
                this._defaults = customDefaults;
            }

            ret = this.callParent([item]);

            if (customDefaults) {
                this._defaults = defaults;
            }

            return ret;
        }
    });
});


Ext.define('Ext.NavigationBar', {
    extend: 'Ext.Container',

    xtype: 'navigationbar',

    requires: [
        'Ext.Button',
        'Ext.Title',
        'Ext.Spacer',
        'Ext.util.SizeMonitor'
    ],

    
    isToolbar: true,

    config: {
        
        baseCls: Ext.baseCSSPrefix + 'toolbar',

        
        ui: 'dark',

        
        title: null,

        
        defaultType: 'button',

        layout: {
            type: 'hbox'
        },

        items: []
    },

    constructor: function() {
        this.refreshTitlePosition = Ext.Function.createThrottled(this.refreshTitlePosition, 50, this);

        this.callParent(arguments);
    },

    initialize: function() {
        this.on({
            painted: 'onPainted',
            erased: 'onErased'
        });
    },

    applyItems: function(items) {
        if (!this.initialized) {
            var SizeMonitor = Ext.util.SizeMonitor,
                leftBox, rightBox, spacer;

            this.leftBox = leftBox = this.add({
                xtype: 'container',
                style: 'position: relative',
                layout: {
                    type: 'hbox',
                    align: 'center'
                }
            });
            this.spacer = spacer = this.add({
                xtype: 'component',
                style: 'position: relative',
                flex: 1
            });
            this.rightBox = rightBox = this.add({
                xtype: 'container',
                style: 'position: relative',
                layout: {
                    type: 'hbox',
                    align: 'center'
                }
            });
            this.titleComponent = this.add({
                xtype: 'title',
                centered: true
            });

            this.sizeMonitors = {
                leftBox: new SizeMonitor({
                    element: leftBox.renderElement,
                    callback: this.refreshTitlePosition,
                    scope: this
                }),
                spacer: new SizeMonitor({
                    element: spacer.renderElement,
                    callback: this.refreshTitlePosition,
                    scope: this
                }),
                rightBox: new SizeMonitor({
                    element: rightBox.renderElement,
                    callback: this.refreshTitlePosition,
                    scope: this
                })
            };

            this.doAdd = this.doBoxAdd;
            this.doInsert = this.doBoxInsert;
        }

        this.callParent(arguments);
    },

    doBoxAdd: function(item) {
        if (item.config.align == 'right') {
            this.rightBox.add(item);
        }
        else {
            this.leftBox.add(item);
        }

        if (this.painted) {
            this.refreshTitlePosition();
        }
    },

    
    doBoxInsert: function(index, item) {
        if (item.config.align == 'right') {
            this.rightBox.add(item);
        }
        else {
            this.leftBox.add(item);
        }
    },

    onPainted: function() {
        var sizeMonitors = this.sizeMonitors;

        this.painted = true;
        this.refreshTitlePosition();

        sizeMonitors.leftBox.refresh();
        sizeMonitors.spacer.refresh();
        sizeMonitors.rightBox.refresh();
    },

    onErased: function() {
        this.painted = false;
    },

    refreshTitlePosition: function() {
        var titleElement = this.titleComponent.renderElement;


            titleElement.setWidth(null);
            titleElement.setLeft(null);



        var spacerBox = this.spacer.renderElement.getPageBox(),
            titleBox = titleElement.getPageBox(),
            widthDiff = titleBox.width - spacerBox.width,
            titleLeft = titleBox.left,
            titleRight = titleBox.right,
            halfWidthDiff, leftDiff, rightDiff;

        if (widthDiff > 0) {
            titleElement.setWidth(spacerBox.width);
            halfWidthDiff = widthDiff / 2;
            titleLeft += halfWidthDiff;
            titleRight -= halfWidthDiff;
        }

        leftDiff = spacerBox.left - titleLeft;
        rightDiff = titleRight - spacerBox.right;

        if (leftDiff > 0) {
            titleElement.setLeft(leftDiff);
        }
        else if (rightDiff > 0) {
            titleElement.setLeft(-rightDiff);
        }

        titleElement.repaint();
    },

    
    updateTitle: function(newTitle) {
        this.titleComponent.setTitle(newTitle);

        this.titleBox = null;

        if (this.painted) {
            this.refreshTitlePosition();
        }
    },

    destroy: function() {
        this.callParent();

        var sizeMonitors = this.sizeMonitors;

        sizeMonitors.leftBox.destroy();
        sizeMonitors.spacer.destroy();
        sizeMonitors.rightBox.destroy();
    }
});


Ext.define('Ext.Panel', {
    isPanel: true,
    extend : 'Ext.Container',
    xtype  : 'panel',
    alternateClassName: 'Ext.lib.Panel',
    config: {
        baseCls: Ext.baseCSSPrefix + 'panel'
    }
});


Ext.define('Ext.SegmentedButton', {
    extend: 'Ext.Container',
    xtype : 'segmentedbutton',
    
    config: {
        
        baseCls: Ext.baseCSSPrefix + 'segmentedbutton',
        
        
        pressedCls: Ext.baseCSSPrefix + 'button-pressed',
        
        
        allowMultiple: false,
        
        
        allowDepress: null,

        
        pressedButtons: null,

        
        layout: {
            type : 'hbox',
            align: 'stretch'
        },

        
        defaultType: 'button'
    },

    

    initialize: function() {
        var me = this;

        me.on({
            delegate: '> button',
            scope   : me,

            release: 'onButtonRelease'
        });

        me.onAfter({
            delegate: '> button',
            scope   : me,

            hiddenchange: 'onButtonHiddenChange'
        });
        
        me.callParent(arguments);
    },

    updateAllowMultiple: function() {
        if (!this.initialized && !this.getInitialConfig().hasOwnProperty('allowDepress')) {
            this.setAllowDepress(true);
        }
    },

    
    applyItems: function() {
        var me = this,
            pressedButtons = [],
            ln, i, item, items;
        
        
        me.callParent(arguments);

        items = this.getItems();
        ln = items.length;

        for (i = 0; i < ln; i++) {
            item = items.items[i];
            if (item.getInitialConfig('pressed')) {
                pressedButtons.push(items.items[i]);
            }
        }

        me.updateFirstAndLastCls(items);

        me.setPressedButtons(pressedButtons);
    },

    
    onButtonRelease: function(button) {
        var me             = this,
            pressedButtons = me.getPressedButtons(),
            ln             = pressedButtons.length,
            buttons        = [],
            alreadyPressed;

        if (!me.disabled) {
            
            if (me.getAllowMultiple()) {
                buttons = buttons.concat(pressedButtons);
            }

            alreadyPressed = buttons.indexOf(button) !== -1;
            
            
            if (alreadyPressed && me.getAllowDepress()) {
                Ext.Array.remove(buttons, button);
            } else if (!alreadyPressed) {
                buttons.push(button);
            }

            me.setPressedButtons(buttons);

            me.fireEvent('toggle', me, button, me.isPressed(button));
        }

        return false;
    },

    
    onButtonHiddenChange: function() {
        this.updateFirstAndLastCls(this.getItems());
    },

    
    updateFirstAndLastCls: function(items) {
        var ln = items.length,
        item, i;

        
        for (i = 0; i < ln; i++) {
            item = items.items[i];
            if (!item.isHidden()) {
                item.addCls(Ext.baseCSSPrefix + 'first');
                break;
            }
        }

        
        for (i = ln - 1; i >= 0; i--) {
            item = items.items[i];
            if (!item.isHidden()) {
                item.addCls(Ext.baseCSSPrefix + 'last');
                break;
            }
        }
    },

    
    applyPressedButtons: function(newButtons, oldButtons) {
        var me    = this,
            array = [],
            button, ln, i;

        if (Ext.isArray(newButtons)) {
            ln = newButtons.length;
            for (i = 0; i< ln; i++) {
                button = me.getComponent(newButtons[i]);
                if (button && array.indexOf(button) === -1) {
                    array.push(button);
                }
            }
        } else {
            button = me.getComponent(newButtons);
            if (button && array.indexOf(button) === -1) {
                array.push(button);
            }
        }

        return array;
    },

    
    updatePressedButtons: function(newButtons, oldButtons) {
        var me    = this,
            items = me.getItems(),
            item, button, ln, i;

        
        ln = items.length;
        for (i = 0; i < ln; i++) {
            item = items.items[i];
            item.removeCls(me.getPressedCls());
        }

        
        ln = newButtons.length;
        for (i = 0; i < ln; i++) {
            button = newButtons[i];
            button.addCls(me.getPressedCls());
        }
    },

    
    isPressed: function(button) {
        var pressedButtons = this.getPressedButtons();
        return pressedButtons.indexOf(button) != -1;
    },

    
    doSetDisabled: function(disabled) {
        var me = this;

        me.items.each(function(item) {
            item.setDisabled(disabled);
        }, me);

        me.callParent(arguments);
    }
}, function() {
    var me = this;
    
    
    Ext.deprecateClassMethod(me, 'setPressed', me.prototype.setPressedButtons, '[Ext.SegmentedButton] setPressed is now deprecated, please use setPressedButtons instead');
    
    
    Ext.deprecateClassMethod(me,
        'getPressed',
        me.prototype.getPressedButtons,
        '[Ext.SegmentedButton] getPressed is now deprecated. Please use getPressedButtons instead.'
    );
    
});


Ext.define('Ext.Sheet', {
    extend: 'Ext.Container',
    alias : 'widget.sheet',

    config: {
        
        baseCls: Ext.baseCSSPrefix + 'sheet',

        
        hidden: true,

        
        modal: true,

        
        centered: true,

        
        hideOnMaskTap: false,

        
        stretchX: null,

        
        stretchY: null,

        
        enter: 'bottom',

        
        exit: 'bottom',

        
        enterAnimation: 'slide',

        
        exitAnimation: 'slide'
    },

    updateStretchX: function(newStretchX) {
        var initialConfig = this.getInitialConfig();
        
        if (newStretchX) {
            this.setLeft(0);
            this.setRight(0);
        } else {
            this.setLeft(initialConfig.left || 'auto');
            this.setRight(initialConfig.right || 'auto');
        }
    },

    updateStretchY: function(newStretchY) {
        var initialConfig = this.getInitialConfig();

        if (newStretchY) {
            this.setTop(0);
            this.setBottom(0);
        } else {
            this.setTop(initialConfig.top || 'auto');
            this.setBottom(initialConfig.bottom || 'auto');
        }
    }
});


Ext.define('Ext.ActionSheet', {
    extend: 'Ext.Sheet',
    alias : 'widget.actionsheet',
    requires: ['Ext.Button'],

    config: {
        
        cls: Ext.baseCSSPrefix + 'sheet-action',

        
        left: 0,

        
        right: 0,

        
        bottom: 0,

        
        centered: false,

        
        height: 'auto',

        
        layout: {
            type : 'vbox',
            align: 'stretch'
        },

        
        defaultType: 'button'
    }
});


Ext.define('Ext.Toolbar', {
    extend: 'Ext.Container',
    xtype : 'toolbar',

    requires: [
        'Ext.Button',
        'Ext.Title',
        'Ext.Spacer'
    ],

    
    isToolbar: true,

    config: {
        
        baseCls: Ext.baseCSSPrefix + 'toolbar',

        
        ui: 'dark',

        
        title: null,

        
        defaultType: 'button',

        

        
        layout: {
            type: 'hbox',
            align: 'center'
        }
    },

    
    applyTitle: function(title) {
        if (typeof title == 'string') {
            title = {title: title};
        }

        return Ext.factory(title, Ext.Title, this.getTitle());
    },

    
    updateTitle: function(newTitle, oldTitle) {
        if (newTitle) {
            this.add(newTitle);
            this.getLayout().setItemFlex(newTitle, 1);
        }

        if (oldTitle) {
            oldTitle.destroy();
        }
    },

    
    showTitle: function() {
        var title = this.getTitle();

        if (title) {
            title.show();
        }
    },

    
    hideTitle: function() {
        var title = this.getTitle();

        if (title) {
            title.hide();
        }
    }

    
    
    
});


Ext.define('Ext.MessageBox', {
    extend  : 'Ext.Sheet',
    requires: [
        'Ext.Toolbar',
        'Ext.field.Text',
        'Ext.field.TextArea'
    ],

    config: {
        
        ui: 'dark',

        
        baseCls: Ext.baseCSSPrefix + 'msgbox',

        
        cls: Ext.baseCSSPrefix + 'panel',

        
        iconCls: null,

        
        enterAnimation: 'pop',

        
        exitAnimation: 'pop',

        
        defaultTextHeight: 75,

        
        title: null,

        
        buttons: null,

        
        msg: null,
        
        
        promptConfig: null,

        
        layout: {
            type: 'vbox',
            pack: 'center'
        }
    },

    statics: {
        OK    : {text: 'OK',     itemId: 'ok',  ui: 'action'},
        YES   : {text: 'Yes',    itemId: 'yes', ui: 'action'},
        NO    : {text: 'No',     itemId: 'no'},
        CANCEL: {text: 'Cancel', itemId: 'cancel'},

        INFO    : Ext.baseCSSPrefix + 'msgbox-info',
        WARNING : Ext.baseCSSPrefix + 'msgbox-warning',
        QUESTION: Ext.baseCSSPrefix + 'msgbox-question',
        ERROR   : Ext.baseCSSPrefix + 'msgbox-error',

        OKCANCEL: [
            {text: 'Cancel', itemId: 'cancel'},
            {text: 'OK',     itemId: 'ok',  ui : 'action'}
        ],
        YESNOCANCEL: [
            {text: 'Cancel', itemId: 'cancel'},
            {text: 'No',     itemId: 'no'},
            {text: 'Yes',    itemId: 'yes', ui: 'action'}
        ],
        YESNO: [
            {text: 'No',  itemId: 'no'},
            {text: 'Yes', itemId: 'yes', ui: 'action'}
        ]
    },

    
    constructor: function(config) {
        config = config || {};

        if (config.hasOwnProperty('prompt')) {
            Ext.applyIf(config, {
                promptConfig: config.prompt
            });

            delete config.prompt;
        }

        if (config.hasOwnProperty('multiline') || config.hasOwnProperty('multiLine')) {
            config.promptConfig = config.promptConfig || {};
            Ext.applyIf(config.promptConfig, {
                multiLine: config.multiline || config.multiLine
            });

            delete config.multiline;
            delete config.multiLine;
        }

        this.callParent([config]);
    },

    
    applyTitle: function(config) {
        if (typeof config == "string") {
            config = {
                title: config
            };
        }

        Ext.applyIf(config, {
            docked: 'top',
            cls   : this.getBaseCls() + '-title'
        });

        return Ext.factory(config, Ext.Toolbar, this.getTitle());
    },

    
    updateTitle: function(newTitle) {
        if (newTitle) {
            this.add(newTitle);
        }
    },

    
    updateButtons: function(newButtons) {
        var me = this;


        if (newButtons) {
            if (me.buttonsToolbar) {
                me.buttonsToolbar.removeAll();
                me.buttonsToolbar.setItems(newButtons);
            } else {
                me.buttonsToolbar = Ext.create('Ext.Toolbar', {
                    docked     : 'bottom',
                    defaultType: 'button',
                    layout     : {
                        type: 'hbox',
                        pack: 'center'
                    },
                    ui         : me.getUi(),
                    cls        : me.getBaseCls() + '-buttons',
                    items      : newButtons
                });

                me.add(me.buttonsToolbar);
            }
        }
    },

    
    applyMsg: function(config) {
        config = {
            html : config,
            cls  : this.getBaseCls() + '-text'
        };

        return Ext.factory(config, Ext.Component, this.getMsg());
    },

    
    updateMsg: function(newMsg) {
        if (newMsg) {
            this.add(newMsg);
        }
    },

    
    updateIconCls: function(newIconCls, oldIconCls) {
        if (newIconCls) {
            var cfg = {
                xtype : 'component',
                docked: 'left',
                width : 40,
                height: 40,
                cls   : newIconCls
            };
            
        }
    },

    
    applyPromptConfig: function(prompt) {
        if (prompt) {
            var config = {
                label: false
            };

            if (typeof prompt == "object") {
                Ext.apply(config, prompt);
            }

            if (config.multiLine) {
                config.height = Ext.isNumber(config.multiLine) ? parseFloat(config.multiLine) : this.getDefaultTextHeight();
                return Ext.factory(config, Ext.field.TextArea, this.getPromptConfig());
            } else {
                return Ext.factory(config, Ext.field.Text, this.getPromptConfig());
            }
        }

        return prompt;
    },

    
    updatePromptConfig: function(newPrompt, oldPrompt) {
        if (newPrompt) {
            this.add(newPrompt);
        }

        if (oldPrompt) {
            this.remove(oldPrompt);
        }
    },

    
    
    onClick: function(button) {
        if (button) {
            var config = button.userConfig || {};

            if (typeof config.fn == 'function') {
                config.fn.call(
                    config.scope || null,
                    button.itemId || button.text,
                    config.input ? config.input.dom.value : null,
                    config
                );
            }

            if (config.cls) {
                    this.el.removeCls(config.cls);
                }

            if (config.input) {
                config.input.dom.blur();
            }
        }

        this.hide();
    },

    
    show: function(initialConfig) {
        if (!initialConfig) {
            return this.callParent();
        }

        var config = Ext.Object.merge({}, {
            value: ''
        }, initialConfig);

        var buttons        = initialConfig.buttons || Ext.MessageBox.OK || [],
            buttonBarItems = [],
            userConfig     = initialConfig;

        Ext.each(buttons, function(buttonConfig) {
            if (!buttonConfig) {
                return;
            }

            buttonBarItems.push(Ext.apply({
                userConfig: userConfig,
                scope     : this,
                handler   : 'onClick'
            }, buttonConfig));
        }, this);

        config.buttons = buttonBarItems;

        this.setConfig(config);

        var prompt = this.getPromptConfig();
        if (prompt) {
            prompt.setValue('');
        }

        this.callParent();

        return this;
    },

    
    alert: function(title, msg, fn, scope) {
        return this.show({
            title       : title,
            msg         : msg,
            buttons     : Ext.MessageBox.OK,
            promptConfig: false,
            fn          : fn,
            scope       : scope,
            iconCls     : Ext.MessageBox.INFO
        });
    },

    
    confirm: function(title, msg, fn, scope) {
        return this.show({
            title       : title,
            msg         : msg,
            buttons     : Ext.MessageBox.YESNO,
            promptConfig: false,
            scope       : scope,
            iconCls     : Ext.MessageBox.QUESTION,
            fn: function(button) {
                fn.call(scope, button);
            }
        });
     },

    
    prompt: function(title, msg, fn, scope, multiLine, value, promptConfig) {
        return this.show({
            title       : title,
            msg         : msg,
            buttons     : Ext.MessageBox.OKCANCEL,
            scope       : scope,
            iconCls     : Ext.MessageBox.QUESTION,
            promptConfig: promptConfig || true,
            multiLine   : multiLine,
            value       : value,
            fn: function(button, inputValue) {
                fn.call(scope, button, inputValue);
            }
        });
    }
}, function() {
    this.override({
        

        
        setIcon: function(iconCls, doLayout){
            Ext.Logger.deprecate("Ext.MessageBox#setIcon is deprecated, use setIconCls instead", 2);
            this.setIconCls(iconCls);
            if (doLayout) {
                this.doComponentLayout();
            }
            return this;
        }
    });

    
    Ext.Msg = Ext.create('Ext.MessageBox');
});



Ext.define('Ext.dataview.DataItem', {
    extend: 'Ext.Container',
    xtype : 'dataitem',

    config: {
        baseCls: Ext.baseCSSPrefix + 'data-item',

        defaultType: 'component',

        
        record: null,

        
        dataMap: {},

        items: [{
            xtype: 'component'
        }]
    },

    
    initialize: function() {
        var me = this;

        me.callParent();

    
        me.element.on({
            tap: 'onTap',
            doubletap: 'onDoubleTap',
            touchstart: 'onTouchStart',
            touchmove: 'onTouchMove',
            touchend: 'onTouchEnd',
            swipe: 'onSwipe',
            scope: me
        });
    },

    
    onTap: function(e) {
        this.fireEvent('tap', this, this.getRecord(), e);
        e.stopPropagation();
    },

    onDoubleTap: function(e) {
        this.fireEvent('doubletap', this, this.getRecord(), e);
    },

    onTouchStart: function(e) {
        this.fireEvent('touchstart', this, this.getRecord(), e);
    },

    onTouchMove: function(e) {
        this.fireEvent('touchmove', this, this.getRecord(), e);
    },

    onTouchEnd: function(e) {
        this.fireEvent('touchend', this, this.getRecord(), e);
    },

    onSwipe: function(e) {
        this.fireEvent('swipe', this, this.getRecord(), e);
    },

    

    updateRecord: function(newRecord) {
        var data = newRecord.getData();
        if (newRecord) {
            Ext.apply(data, Ext.dataview.DataView.prepareAssociatedData(newRecord));
        }
        var me = this,
            items = me.getItems(),
            item = items.first(),
            dataMap = me.getDataMap(),
            componentName, component, setterMap, setterName;
        if (!item) {
            return;
        }
        for (componentName in dataMap) {
            setterMap = dataMap[componentName];
            component = me[componentName]();
            if (component) {
                for (setterName in setterMap) {
                    if (component[setterName]) {
                        component[setterName](data[setterMap[setterName]]);
                    }
                }
            }
        }
        
        
        item.updateData(data);
    }
});


Ext.define('Ext.dataview.DataView', {
    extend: 'Ext.Container',

    alternateClassName: 'Ext.DataView',

    mixins: ['Ext.mixin.Selectable'],

    xtype: 'dataview',

    requires: [
        'Ext.data.StoreManager'
    ],

    config: {
        
        store: null,

        
        baseCls: Ext.baseCSSPrefix + 'dataview',

        
        emptyText: null,

        
        deferEmptyText: true,

        
        itemTpl: '<div>{text}</div>',

        
        pressedCls : 'x-item-pressed',

        
        selectedCls: 'x-item-selected',

        
        triggerEvent: 'tap',

        
        triggerCtEvent: 'tap',

        
        deselectOnContainerClick: true,

        
        scrollable: true,

        
        pressedDelay: 100,

        
        loadingText: 'Loading...'
    },

    inheritableStatics: {
        
        prepareAssociatedData: function(record, ids) {
            
            ids = ids || [];

            var associations     = record.associations.items,
                associationCount = associations.length,
                associationData  = {},
                i = 0,
                j = 0,
                associatedStore, associatedRecords, associatedRecord,
                associatedRecordCount, association, internalId;

            for (; i < associationCount; i++) {
                association = associations[i];

                
                associatedStore = record[association.storeName];

                
                associationData[association.name] = [];

                
                if (associatedStore && associatedStore.data.length > 0) {
                    associatedRecords = associatedStore.data.items;
                    associatedRecordCount = associatedRecords.length;

                    
                    for (; j < associatedRecordCount; j++) {
                        associatedRecord = associatedRecords[j];
                        internalId = associatedRecord.internalId;

                        
                        
                        if (ids.indexOf(internalId) == -1) {
                            ids.push(internalId);

                            associationData[association.name][j] = associatedRecord.data;
                            Ext.apply(associationData[association.name][j], this.prepareAssociatedData(associatedRecord, ids));
                        }
                    }
                }
            }

            return associationData;
        }
    },

    constructor: function() {
        this.mixins.selectable.constructor.apply(this, arguments);
        this.callParent(arguments);
    },

    storeEventHooks: {
        beforeload: 'onBeforeLoad',
        load      : 'refresh',
        sort      : 'refresh',
        filter    : 'refresh',
        add       : 'onStoreAdd',
        remove    : 'onStoreRemove',
        update    : 'onStoreUpdate',
        clear     : 'onStoreClear'
    },

    doInitialize: function() {
        var me = this,
            triggerObj = {
                delegate: '> div',
                scope: me
            },
            clearObj = {
                scope: me
            },
            elementContainerElement;


        me.getViewItems();
        elementContainerElement = me.elementContainer.element;

        clearObj[me.getTriggerCtEvent()] = 'onContainerTrigger';
        me.element.on(clearObj);

        triggerObj[me.getTriggerEvent()] = 'onItemTrigger';
        elementContainerElement.on(triggerObj);

        elementContainerElement.on({
            delegate: '> div',
            scope   : me,

            touchstart: 'onItemTouchStart',
            touchend  : 'onItemTouchEnd',
            tap       : 'onItemTap',
            touchmove : 'onItemTouchMove',
            doubletap : 'onItemDoubleTap',
            swipe     : 'onItemSwipe'
        });
    },

    //@private

    initialize: function() {
        this.doInitialize();
        this.callParent(arguments);
    },

    
    onItemTrigger: function(e) {
        this.selectWithEvent(this.getStore().getAt(e.getTarget().getAttribute('itemIndex')), e);
    },

    
    onContainerTrigger: function(e) {
        var me = this;
        if (e.target != me.element.dom) {
            return;
        }
        if (me.getDeselectOnContainerClick() && me.getStore()) {
            me.deselectAll();
        }
    },

    doAddPressedCls: function(record) {
        var me = this,
        item = me.getViewItems()[me.getStore().indexOf(record)];
        Ext.get(item).addCls(me.getPressedCls());
    },

    onItemTouchStart: function(e) {
        var me = this,
            target = e.getTarget(),
            index = target.getAttribute('itemIndex'),
            store = me.getStore(),
            record = store && store.getAt(index),
            pressedDelay = me.getPressedDelay(),
            item = Ext.get(target);

        if (record) {
            if (pressedDelay > 0) {
                me.pressedTimeout = Ext.defer(me.doAddPressedCls, pressedDelay, me, [record]);
            }
            else {
                me.doAddPressedCls(record);
            }
        }

        item.on({
            touchmove: 'onItemTouchMove',
            scope   : me,
            single: true
        });

        me.fireAction('itemtouchstart', [me, index, target, e], 'doItemTouchStart');
    },

    doItemTouchStart: Ext.emptyFn,

    onItemTouchEnd: function(e) {
        var me = this,
            target = e.getTarget(),
            index = target.getAttribute('itemIndex'),
            store = me.getStore(),
            record = store && store.getAt(index),
            item = Ext.get(target);

        if (this.hasOwnProperty('pressedTimeout')) {
            clearTimeout(this.pressedTimeout);
            delete this.pressedTimeout;
        }

        if (record) {
            Ext.get(target).removeCls(me.getPressedCls());
        }

        item.un({
            touchmove: 'onItemTouchMove',
            scope   : me
        });

        me.fireAction('itemtouchend', [me, index, target, e], 'doItemTouchEnd');
    },

    doItemTouchEnd: Ext.emptyFn,

    onItemTouchMove: function(e) {
        var me = this,
            target = e.getTarget(),
            index = target.getAttribute('itemIndex'),
            store = me.getStore(),
            record = store && store.getAt(index),
            item = Ext.get(target);

        if (me.hasOwnProperty('pressedTimeout')) {
            clearTimeout(me.pressedTimeout);
            delete me.pressedTimeout;
        }

        if (record) {
            item.removeCls(me.getPressedCls());
        }
    },

    onItemTap: function(e) {
        var me = this,
            target = e.getTarget(),
            index = target.getAttribute('itemIndex'),
            item = Ext.get(target);

        me.fireAction('itemtap', [me, index, item, e], 'doItemTap');
    },

    doItemTap: Ext.emptyFn,

    onItemDoubleTap: function(e) {
        var me = this,
            target = e.getTarget(),
            index = target.getAttribute('itemIndex'),
            item = Ext.get(target);

        me.fireAction('itemdoubletap', [me, index, item, e], 'doItemDoubleTap');
    },

    doItemDoubleTap: Ext.emptyFn,

    onItemSwipe: function(e) {
        var me = this,
            target = e.getTarget(),
            index = target.getAttribute('itemIndex'),
            item = Ext.get(target);

        me.fireAction('itemswipe', [me, index, item, e], 'doItemSwipe');
    },

    doItemSwipe: Ext.emptyFn,

    
    onItemSelect: function(record, suppressEvent) {
        var me = this;
        if (suppressEvent) {
            me.doItemSelect(me, record);
        } else {
            me.fireAction('select', [me, record], 'doItemSelect');
        }
    },

    
    doItemSelect: function(me, record) {
        var item = Ext.get(me.getViewItems()[me.getStore().indexOf(record)]);
        item.removeCls(me.getPressedCls());
        item.addCls(me.getSelectedCls());
    },

    
    onItemDeselect: function(record, suppressEvent) {
        var me = this;
        if (suppressEvent) {
            me.doItemDeSelect(me, record);
        }
        else {
            me.fireAction('deselect', [me, record, suppressEvent], 'doItemDeSelect');
        }
    },

    doItemDeSelect: function(me, record) {
        var item = Ext.get(me.getViewItems()[me.getStore().indexOf(record)]);
        if (item) {
            item.removeCls([me.getPressedCls(), me.getSelectedCls()]);
        }
    },

    updateData: function(data) {
        var store = this.getStore();
        if (!store) {
            this.setStore(Ext.create('Ext.data.ArrayStore', {
                fields: data
            }));
        } else {
            store.add(data);
        }
    },

    applyStore: function(store) {
        var me = this,
            loadMask = me.loadMask,
            bindEvents = Ext.apply({}, me.storeEventHooks, { scope: me });

        if (store) {
            store = Ext.data.StoreManager.lookup(store);
            if (store && Ext.isObject(store) && store.isStore) {
                store.on(bindEvents);
                if (loadMask) {
                    loadMask.bindStore(store);
                }
            }
        }

        return store;
    },

    updateStore: function(newStore, oldStore) {
        var me = this,
            loadMask = me.loadMask,
            bindEvents = Ext.apply({}, me.storeEventHooks, { scope: me });

        if (oldStore && Ext.isObject(oldStore) && oldStore.isStore) {
            if (oldStore.autoDestroy) {
                oldStore.destroy();
            }
            else {
                oldStore.un(bindEvents);
            }
        }

        if (newStore) {
            me.refresh();
        }
        else if (loadMask) {
            loadMask.bindStore(null);
        }
    },

    onBeforeLoad: function() {
        var loadingText = this.getLoadingText();
        if (loadingText) {
            this.mask(loadingText, null, true);
        }
    },

    
    refresh: function() {
        var me = this;

        
        this.unmask();

        if (!me.getStore()) {
            if (!this.getDeferEmptyText()) {
                this.doEmptyText();
            }
            return;
        }
        me.fireAction('refresh', [me], 'doRefresh');
    },

    applyItemTpl: function(config) {
        return (Ext.isObject(config) && config.isTemplate) ? config : new Ext.XTemplate(config);
    },

    onAfterRender: function() {
        var me = this;
        me.callParent(arguments);
        me.updateStore(me.getStore());
    },

    updateListItem: function(record, item) {
        var data = record.getData();
        if (record) {
            
            Ext.apply(data, this.self.prepareAssociatedData(record));
        }
        var index = this.getStore().indexOf(record),
            html;

        item.setAttribute('itemIndex', index);
        html = this.getItemTpl().apply(data);
        item.innerHTML = html;
    },

    addListItem: function(index, record) {
        var data = record.getData();

        if (record) {
            
            Ext.apply(data, this.self.prepareAssociatedData(record));
        }
        var element = this.elementContainer.element,
            childNodes = element.dom.childNodes,
            ln = childNodes.length,
            wrapElement;

        wrapElement = Ext.Element.create(this.getItemElementConfig(index, data));

        if (!ln || index == ln) {
            wrapElement.appendTo(element);
        } else {
            wrapElement.insertBefore(childNodes[index]);
        }
    },

    getItemElementConfig: function(index, data) {
        return {
            cls: this.getBaseCls() + '-item',
            itemIndex: index,
            html: this.getItemTpl().apply(data)
        };
    },

    
    moveItemsToCache: function(from, to) {
        var me = this,
            items = me.getViewItems(),
            i = to - from,
            item;

        for (; i >= 0; i--) {
            item = items[from + i];
            item.parentNode.removeChild(item);
        }
        if (me.getViewItems().length == 0) {
            this.doEmptyText();
        }
    },

    doEmptyText: function() {
        var emptyText = this.getEmptyText();
        if (emptyText) {
            this.elementContainer.setHtml('');
            this.elementContainer.setHtml(emptyText);
        }
    },

    
    moveItemsFromCache: function(records, index) {
        var me = this,
            ln = records.length,
            i = 0,
            record;

        for (; i < ln; i++) {
            record = records[i];
            me.addListItem(index + i, record);
        }
        
        me.elementContainer.setHtml('');
    },

    getViewItems: function() {
        if (!this.elementContainer) {
            this.elementContainer = this.add(new Ext.Component());
        }
        return this.elementContainer.element.dom.childNodes;
    },

    doRefresh: function(me) {
        var store = me.getStore(),
            records = store.getRange(),
            items = me.getViewItems(),
            recordsLn = records.length,
            itemsLn = items.length,
            deltaLn = recordsLn - itemsLn,
            i, item;

        
        if (recordsLn < 1) {
            me.onStoreClear();
            return;
        }

        
        if (deltaLn < 0) {
            this.moveItemsToCache(itemsLn + deltaLn, itemsLn - 1);
            
            items = me.getViewItems();
            itemsLn = items.length;
        }
        
        else if (deltaLn > 0) {
            this.doCreateItems(store.getRange(itemsLn), itemsLn);
        }

        
        for (i = 0; i < itemsLn; i++) {
            item = items[i];
            me.updateListItem(records[i], item);
        }
    },

    doCreateItems: function(records, ln) {
        this.moveItemsFromCache(records, ln);
    },

    onStoreClear: function() {
        var me = this,
            items = me.getViewItems();

        this.moveItemsToCache(0, items.length - 1);
        this.doEmptyText();
    },

    
    onStoreAdd: function(store, records, index) {
        if (records) {
            this.doCreateItems(records, index);
        }
    },

    
    onStoreRemove: function(store, record, index) {
        this.moveItemsToCache(index, index);
    },

    
    onStoreUpdate: function(store, record) {
        
        this.updateListItem(record, this.getViewItems()[store.indexOf(record)]);
    }
}, function() {

    
    Ext.deprecateClassMethod(this, 'bindStore', this.prototype.setStore, "'bindStore()' is deprecated, please use 'setStore' instead");
});


Ext.define('Ext.dataview.ComponentView', {
    extend: 'Ext.dataview.DataView',

    alternateClassName: 'Ext.ComponentView',

    xtype: 'componentview',

    requires: [
        'Ext.dataview.DataItem'
    ],

    config: {
        
        defaultType: 'dataitem',

        
        itemConfig: {},

        maxItemCache: 20
    },

    constructor: function() {
        this.itemCache = [];
        this.callParent(arguments);
    },

    //@private

    doInitialize: function() {
        var me = this,
            triggerObj = {
                delegate: '> ' + me.getDefaultType(),
                scope: me
            },
            clearObj = {
                scope: me
            };

        clearObj[me.getTriggerCtEvent()] = 'onContainerTrigger';
        me.element.on(clearObj);

        triggerObj[me.getTriggerEvent()] = 'onItemTrigger';
        me.on(triggerObj);

        me.on({
            delegate: '> ' + me.getDefaultType(),
            scope   : me,

            touchstart: 'onItemTouchStart',
            touchend: 'onItemTouchEnd',
            tap: 'onItemTap',
            touchmove: 'onItemTouchMove',
            doubletap: 'onItemDoubleTap',
            swipe: 'onItemSwipe'
        });
    },

    
    onItemTrigger: function(item, record, e) {
        this.selectWithEvent(record, e);
    },

    
    onContainerTrigger: function() {
        var me = this;
        if (me.getDeselectOnContainerClick() && me.getStore()) {
            me.deselectAll();
        }
    },

    
    doItemSelect: function(me, record) {
        var item = me.getViewItems()[me.getStore().indexOf(record)];
        item.removeCls(me.getPressedCls());
        item.addCls(me.getSelectedCls());
    },

    doItemDeSelect: function(me, record) {
        var item = me.getViewItems()[me.getStore().indexOf(record)];
        if (item) {
            item.removeCls([me.getPressedCls(), me.getSelectedCls()]);
        }
    },

    doAddPressedCls: function(record) {
        var me = this,
        item = me.getViewItems()[me.getStore().indexOf(record)];
        item.addCls(me.getPressedCls());
    },

    onItemTouchStart: function(item, record, e) {
        var me = this,
            pressedDelay = me.getPressedDelay();
        if (record) {
            if (pressedDelay > 0) {
                me.pressedTimeout = Ext.defer(me.doAddPressedCls, pressedDelay, me, [record]);
            }
            else {
                me.doAddPressedCls(record);
            }
        }
        item.on({
            touchmove: 'onItemTouchMove',
            scope   : me,
            single: true
        });
        me.fireAction('itemtouchstart', [me, me.getViewItems().indexOf(item), item, e], 'doItemTouchStart');
    },

    onItemTouchEnd: function(item, record, e) {
        var me = this;
        if (this.hasOwnProperty('pressedTimeout')) {
            clearTimeout(this.pressedTimeout);
            delete this.pressedTimeout;
        }
        if (record) {
            cmp = me.getViewItems()[me.getStore().indexOf(record)];
            cmp.removeCls(me.getPressedCls());
        }
        item.un({
            touchmove: 'onItemTouchMove',
            scope   : me
        });
        me.fireAction('itemtouchend', [me, me.getViewItems().indexOf(item), item, e], 'doItemTouchEnd');
    },

    onItemTouchMove: function(item, record, e) {
        var me = this;
        if (me.hasOwnProperty('pressedTimeout')) {
            clearTimeout(me.pressedTimeout);
            delete me.pressedTimeout;
        }
        if (record) {
            me.getViewItems()[me.getStore().indexOf(record)].removeCls(me.getPressedCls());
        }
    },

    onItemTap: function(item, record, e) {
        var me = this;
        me.fireAction('itemtap', [me, me.getViewItems().indexOf(item), item, e], 'doItemTap');
    },

    onItemDoubleTap: function(item, record, e) {
        var me = this;
        me.fireAction('itemdoubletap', [me, me.getViewItems().indexOf(item), item, e], 'doItemDoubleTap');
    },

    onItemSwipe: function(item, record, e) {
        var me = this;
        me.fireAction('itemswipe', [me, me.getViewItems().indexOf(item), item, e], 'doItemSwipe');
    },

    moveItemsToCache: function(from, to) {
        var me = this,
            maxItemCache = me.getMaxItemCache(),
            items = me.getViewItems(),
            itemCache = me.itemCache,
            cacheLn = itemCache.length,
            i = to - from,
            item;

        for (; i >= 0; i--) {
            item = items[from + i];
            if (cacheLn !== maxItemCache) {
                me.remove(item, false);
                item.removeCls([me.getPressedCls(), me.getSelectedCls()]);
                itemCache.push(item);
                cacheLn++;
            }
            else {
                item.destroy();
            }
        }
    },

    moveItemsFromCache: function(records) {
        var me = this,
            ln = records.length,
            xtype = me.getDefaultType(),
            itemConfig = me.getItemConfig(),
            itemCache = me.itemCache,
            cacheLn = itemCache.length,
            items = [],
            i = 0,
            item, record;

        for (; i < ln; i++) {
            record = records[i];
            if (cacheLn) {
                cacheLn--;
                item = itemCache.pop();
                item.setRecord(record);
                items.push(item);
            }
            else {
                items.push(me.getDataItemConfig(xtype, record, itemConfig));
            }
        }
        return items;
    },

    getViewItems: function() {
        return this.getInnerItems();
    },

    updateListItem: function(record, item) {
        if (item.setRecord) {
            item.setRecord(record);
        }
    },

    doCreateItems: function(records, ln) {
        this.add(this.moveItemsFromCache(records, ln));
    },

    getDataItemConfig: function(xtype, record, itemConfig) {
        return {
            xtype: xtype,
            record: record,
            defaults: itemConfig
        };
    }
});


Ext.define('Ext.carousel.Carousel', {
    extend: 'Ext.dataview.ComponentView',
    xtype: 'carousel',

    alternateClassName: 'Ext.Carousel',

    requires: ['Ext.carousel.Indicator', 'Ext.util.SizeMonitor'],

    config: {
        
        baseCls: Ext.baseCSSPrefix + 'carousel',

        
        itemCls: Ext.baseCSSPrefix + 'carousel-item',

        
        ui: 'dark',

        
        indicator: true,

        
        direction: 'horizontal',

        scrollable: false
    },

    
    initialize: function() {
        this.callParent();

        this.element.on({
            drag: 'onDrag',
            dragstart: 'onDragStart',
            dragend: 'onDragEnd',

            scope: this
        });

        this.on({
            painted: 'onPainted',
            activeitemchange: 'onActiveItemChange',
            scope: this
        });
    },

    
    updateCardSize: function() {
        this.currentSize = this.element.getSize();
        this.currentScroll = {
            x: 0,
            y: 0
        };

        var cards = this.getInnerItems(),
            ln = cards.length,
            i, card;

        for (i = 0; i < ln; i++) {
            card = cards[i];
            if (this.isIndexInRange(i)) {
                this.updateCardPosition(card);
            }
        }
    },

    
    onPainted: function() {
        this.updateCardSize();

        if (!this.sizeMonitor) {
            
            this.sizeMonitor = new Ext.util.SizeMonitor({
                element: this.element,
                callback: this.updateCardSize,
                scope: this
            });
        } else {
            this.sizeMonitor.refresh();
        }

        this.onActiveItemChange(this, this.getActiveItem());
    },

    
    onAdd: function(item, index) {
        if (!item.isInnerItem()) {
            return this.callParent(arguments);
        }

        item.element.addCls(this.getItemCls());
        item.hide();

        this.callParent(arguments);

        var isCardInRange = this.isCardInRange(item),
            activeItem = this.getActiveItem(),
            indicator = this.getIndicator();

        if (isCardInRange) {
            if (this.isPainted() && activeItem !== item) {
                this.updateCardPosition(item);
            }

            item.show();
        }

        if (indicator) {
            indicator.addIndicator();
        }
    },

    
    onRemove: function(item, index) {
        if (item.isInnerItem()) {
            if (!this.isCardInRange(item)) {
                item.show();
            }
            item.element.removeCls(this.getItemCls());
        }

        this.callParent(arguments);

        var indicator = this.getIndicator();
        if (indicator) {
            indicator.removeIndicator();
        }
    },

    
    updateCardPosition: function(card, index, activeIndex) {
        card.element.dom.style.webkitTransform = this.getCardTransform(this.getCardOffset(card, index, activeIndex));
    },

    
    getCardTransform: function(offset) {
        if (this.getDirection() === 'horizontal') {
            return 'translate3d(' + offset + 'px, 0px, 0px)';
        } else {
            return 'translate3d(0px, ' + offset + 'px, 0px)';
        }
    },

    
    getCardOffset: function(card, index, activeIndex) {
        var cardOffset = this.getCardIndexOffset(card, index, activeIndex),
            currentSize = this.currentSize || {
                width: 0,
                height: 0
            },
            currentScroll = this.currentScroll;

        return this.getDirection() === 'horizontal' ?
            (cardOffset * currentSize.width) + currentScroll.x :
            (cardOffset * currentSize.height) + currentScroll.y;
    },

    
    getCardIndexOffset: function(card, index, activeIndex) {
        if (index === undefined) {
            index = this.getInnerItems().indexOf(card);
        }
        if (activeIndex === undefined) {
            activeIndex = this.getActiveIndex();
        }
        return index - activeIndex;
    },

    
    isCardInRange: function(card) {
        return Math.abs(this.getCardIndexOffset(card)) <= 1;
    },

    
    isIndexInRange: function(index, activeIndex) {
        if (activeIndex === undefined) {
            activeIndex = this.getActiveIndex();
        }
        return Math.abs(index - activeIndex) <= 1;
    },

    
    getActiveIndex: function() {
        return this.getInnerItems().indexOf(this.getActiveItem());
    },

    
    updateItemCls: function(newItemCls, oldItemCls) {
        if (oldItemCls) {
            
        }
    },

    
    updateDirection: function(direction) {
        
    },

    
    applyIndicator: function(indicator) {
        return Ext.factory(indicator ? {direction: this.getDirection()} : null, Ext.carousel.Indicator, this.getIndicator());
    },

    
    updateIndicator: function(indicator) {
        if (indicator) {
            this.add(indicator);

            var activeIndex = this.getActiveIndex();

            if (activeIndex !== -1) {
                indicator.setActiveIndex(activeIndex);
            }

            indicator.setUi(this.getUi());

            indicator.on({
                next: 'next',
                previous: 'previous',
                scope: this
            });
        }
    },

    
    onDrag: function(e, t) {
        var activeIndex = this.getActiveIndex(),
            cards = this.getInnerItems(),
            ln = cards.length,
            deltaX = e.deltaX,
            deltaY = e.deltaY,
            i, card;

        this.currentScroll = {
            x: deltaX,
            y: deltaY
        };

        

        
        if (this.getDirection() == 'horizontal') {
            if (
                
                (activeIndex == 0 && deltaX > 0) ||
                
                (activeIndex == cards.length - 1 && deltaX < 0)
            ) {
                
                this.currentScroll.x = deltaX / 2;
            }
        }
        
        else {
            if (
                
                (activeIndex == 0 && deltaY > 0) ||
                
                (activeIndex == cards.length - 1 && deltaY < 0)
            ) {
                
                this.currentScroll.y = deltaY / 2;
            }
        }

        
        for (i = 0; i < ln; i++) {
            if (this.isIndexInRange(i, activeIndex)) {
                this.updateCardPosition(cards[i]);
            }
        }
    },

    
    onDragStart: function(e) {
        e.stopPropagation();
    },

    
    onDragEnd: function(e, t) {
        var cards = this.getInnerItems(),
            activeIndex = this.getActiveIndex(),
            previousDelta, deltaOffset;

        if (this.getDirection() === 'horizontal') {
            deltaOffset = e.deltaX;
            previousDelta = e.previousDeltaX;
        } else {
            deltaOffset = e.deltaY;
            previousDelta = e.previousDeltaY;
        }

        
        if (deltaOffset < 0 && Math.abs(deltaOffset) > 3 && previousDelta <= 0 && cards[activeIndex+1]) {
            this.next();
        }
        
        else if (deltaOffset > 0 && Math.abs(deltaOffset) > 3 && previousDelta >= 0 && cards[activeIndex-1]) {
            this.previous();
        } else {
            
            this.onActiveItemChange(this, this.getActiveItem());
        }
    },

    
    onActiveItemChange: function(carousel, activeItem) {
        var cards = this.getInnerItems(),
            ln = cards.length,
            activeIndex = cards.indexOf(activeItem),
            indicator = this.getIndicator(),
            i, card;

        if (activeIndex == -1) {
            return;
        }

        this.currentScroll = {
            x: 0,
            y: 0
        };

        for (i = 0; i < ln; i++) {
            card = cards[i];
            if (this.isIndexInRange(i, activeIndex)) {
                this.updateCardPosition(card, i, activeIndex);
                card.show();
            } else {
                card.hide();
            }
        }

        if (indicator) {
            indicator.setActiveIndex(activeIndex);
        }
    },

    
    next: function() {
        var next = this.getInnerItems()[this.getActiveIndex()+1];
        if (next) {
            this.setActiveItem(next);
        }
        return this;
    },

    
    previous: function() {
        var prev = this.getInnerItems()[this.getActiveIndex()-1];
        if (prev) {
            this.setActiveItem(prev);
        }
        return this;
    },

    
    destroy: function() {
        if (this.sizeMonitor) {
            this.sizeMonitor.destroy();
        }
        this.callParent();
    }
}, function() {

    
    Ext.deprecateClassMethod(this, 'prev', 'previous');

    
    Ext.deprecateClassMethod(this, 'isHorizontal', function() {
        return this.getDirection() == 'horizontal';
    }, 'isHorizontal is deprecated, please use this.getDirection()');

    
    Ext.deprecateClassMethod(this, 'isVertical', function() {
        return this.getDirection() == 'vertical';
    }, 'isVertical is deprecated, please use this.getDirection()');

});

Ext.define('Ext.dataview.List', {
    alternateClassName: 'Ext.List',
    extend: 'Ext.dataview.DataView',
    xtype : 'list',

    requires: [
        'Ext.dataview.IndexBar',
        'Ext.dataview.ListItemHeader'
    ],

    config: {
        
        indexBar: false,

        disclosure: null,

        icon: null,

        
        clearSelectionOnDeactivate: true,

        
        preventSelectionOnDisclose: true,

        
        baseCls: Ext.baseCSSPrefix + 'list',

        
        pinHeaders: true,

        grouped: false,

        
        onItemDisclosure: null
    },

    constructor: function() {
        this.translateHeader = (Ext.os.is.Android2) ? this.translateHeaderCssPosition : this.translateHeaderTransform;
        this.callParent(arguments);
    },

    initialize: function() {
        var me = this;
        me.callParent(arguments);
        me.elementContainer.element.on({
            delegate: '.' + this.getBaseCls() + '-disclosure',
            tap: 'handleItemDisclosure',
            scope: me
        });
    },

    applyIndexBar: function(indexBar) {
        if (this.getGrouped()) {
            return Ext.factory(indexBar, Ext.dataview.IndexBar, this.getIndexBar());
        }
    },

    updateIndexBar: function(indexBar) {
        if (indexBar && this.getScrollable()) {
            this.getScrollableBehavior().getScrollView().getElement().appendChild(indexBar.renderElement);

            indexBar.on({
                index: 'onIndex',
                scope: this
            });

            this.addCls(this.getBaseCls() + '-indexed');
        }
    },

    updatePinHeaders: function(pinnedHeaders) {
        var scrollable = this.getScrollable(),
            store = this.getStore(),
            scrollView = this.getScrollableBehavior().getScrollView(),
            scrollViewElement = scrollView.getElement(),
            header, scroller;

        if (scrollable && this.getGrouped()) {
            scroller = scrollable.getScroller();
            if (pinnedHeaders) {
                scroller.on({
                    refresh: 'doRefreshHeaders',
                    scroll: 'onScroll',
                    scope: this
                });

                store.on({
                    datachanged: 'doRefreshHeaders',
                    scope: this
                });

                this.header = header = Ext.create('Ext.dataview.ListItemHeader', {html: ' ', cls: 'x-list-header-swap'});
                scrollViewElement.dom.insertBefore(header.element.dom, scroller.getContainer().dom.nextSibling);
            } else {
                scroller.un({
                    refresh: 'onScrollerRefresh',
                    scroll: 'onScroll',
                    scope: this
                });

                store.un({
                    datachanged: 'doRefreshHeaders',
                    scope: this
                });

                if (this.header) {
                    this.header.destroy();
                }
            }
        }
    },

    onStoreClear: function() {
        this.callParent();
        if (this.header) {
            this.header.destroy();
        }
    },

    
    getClosestGroups : function() {
        var groups = this.pinHeaderInfo.offsets,
            pos = this.getScrollable().getScroller().position,
            ln = groups.length,
            i = 0,
            group, current, next;

        for (; i < ln; i++) {
            group = groups[i];
            if (group.offset > pos.y) {
                next = group;
                break;
            }
            current = group;
        }

        return {
            current: current,
            next: next
        };
    },

    doRefreshHeaders: function() {
        var headerIndices = this.findGroupHeaderIndices(),
            ln = headerIndices.length,
            items = this.getViewItems(),
            headerInfo = this.pinHeaderInfo = {offsets: []},
            headerOffsets = headerInfo.offsets,
            i, headerItem, header;

        if (ln) {
            for (i = 0; i < ln; i++) {
                headerItem = items[headerIndices[i]];
                if (headerItem) {
                    header = this.getItemHeader(headerItem);

                    headerOffsets.push({
                        header: header,
                        offset: headerItem.offsetTop
                    });
                }
            }

            headerInfo.closest = this.getClosestGroups();
            this.setActiveGroup(headerInfo.closest.current);
            if (header) {
                headerInfo.headerHeight = Ext.get(header).getHeight();
            }
        }
    },

    getItemHeader: function(item) {
        return item.childNodes[0];
    },

    onScroll: function(scroller, x, y) {
        var me = this,
            headerInfo = me.pinHeaderInfo,
            closest = headerInfo.closest,
            activeGroup = me.activeGroup,
            headerHeight = headerInfo.headerHeight,
            next = closest.next,
            current = closest.current;

        if (y <= 0) {
            if (activeGroup) {
                me.setActiveGroup(false);
                closest.next = current;
            }
            return;
        }
        else if ((next && y > next.offset) || (current && y < current.offset)) {
            closest = headerInfo.closest = this.getClosestGroups();
            next = closest.next;
            current = closest.current;
            this.setActiveGroup(current);
        }

        if (next && y > 0 && next.offset - y <= headerHeight) {
            var headerOffset = headerHeight - (next.offset - y);
            this.translateHeader(headerOffset);
        }
        else {
            this.translateHeader(null);
        }
    },

    translateHeaderTransform: function(offset) {
        this.header.renderElement.dom.style.webkitTransform = (offset === null) ? null : 'translate3d(0px, -' + offset + 'px, 0px)';
    },

    translateHeaderCssPosition: function(offset) {
        this.header.renderElement.dom.style.top = (offset === null) ? null : '-' + Math.round(offset) + 'px';
    },

    
    setActiveGroup : function(group) {
        var me = this;
        if (group) {
            if (!me.activeGroup || me.activeGroup.header != group.header) {
                me.header.setHtml(group.header.innerHTML);
                me.header.show();
            }
        } else {
            me.header.hide();
        }

        this.activeGroup = group;
    },

    onIndex: function(index) {
        var key = index.toLowerCase(),
            store = this.getStore(),
            groups = store.getGroups(),
            ln = groups.length,
            scroller = this.getScrollable().getScroller(),
            group, i, closest, id, item;

        for (i = 0; i < ln; i++) {
            group = groups[i];
            id = group.name.toLowerCase();
            if (id == key || id > key) {
                closest = group;
                break;
            }
            else {
                closest = group;
            }
        }

        item = this.getViewItems()[store.indexOf(closest.children[0])];

        
        scroller.stopAnimation();

        
        var containerSize = scroller.getContainerSize().y,
            size = scroller.getSize().y,
            maxOffset = size - containerSize,
            offset = (item.offsetTop > maxOffset) ? maxOffset : item.offsetTop;

        scroller.scrollTo(0, offset);
    },

    applyOnItemDisclosure: function(config) {
        if (Ext.isFunction(config)) {
            return {
                scope: this,
                handler: config
            };
        }
        if (Ext.isObject(config)) {
            return config;
        }
        return null;
    },

    getDisclosure: function() {
        var value = this._disclosure,
            onItemDisclosure = this.getOnItemDisclosure();

        if (onItemDisclosure && onItemDisclosure != value) {
            value = true;
            this.setDisclosure(value);
        }

        return value;
    },

    updateOnItemDisclosure: function(newOnItemDisclosure) {
        
        if (newOnItemDisclosure) {
            this.setDisclosure(true);
        }
    },

    handleItemDisclosure: function(e) {
        var me = this,
            item = e.getTarget().parentNode,
            index = item.getAttribute('itemIndex'),
            record = me.getStore().getAt(index),
            onItemDisclosure = me.getOnItemDisclosure();

        if (me.getPreventSelectionOnDisclose()) {
            e.stopEvent();
        }
        me.fireAction('disclose', [record, item, index, e], 'doDisclose');

        if (onItemDisclosure && onItemDisclosure.handler) {
            onItemDisclosure.handler.call(me, record, item, index);
        }
    },

    updateBaseCls: function(newBaseCls, oldBaseCls) {
        var me = this;
        me.callParent(arguments);
        me.itemClsShortCache = newBaseCls + '-item';

        me.headerClsShortCache = newBaseCls + '-header';
        me.headerClsCache = '.' + me.headerClsShortCache;

        me.labelClsShortCache = newBaseCls + '-item-label';
        me.labelClsCache = '.' + me.labelClsShortCache;

        me.disclosureClsShortCache = newBaseCls + '-disclosure';
        me.disclosureClsCache = '.' + me.disclosureClsShortCache;

        me.iconClsShortCache = newBaseCls + '-icon';
        me.iconClsCache = '.' + me.iconClsShortCache;
    },

    hiddenDisplayCache: Ext.baseCSSPrefix + 'hidden-display',

    doDisclose: Ext.emptyFn,

    updateListItem: function(record, item) {
        var extItem = Ext.get(item),
            innerItem = extItem.down(this.labelClsCache, true),
            index = this.getStore().indexOf(record),
            data = record.data,
            disclosure = data && data.hasOwnProperty('disclosure'),
            iconSrc = data && data.hasOwnProperty('iconSrc'),
            disclosureEl, iconEl;

        item.setAttribute('itemIndex', index);
        innerItem.innerHTML = this.getItemTpl().apply(data);

        if (this.getDisclosure() && disclosure) {
            disclosureEl = extItem.down(this.disclosureClsCache);
            disclosureEl[disclosure ? 'removeCls' : 'addCls'](this.hiddenDisplayCache);
        }

        if (this.getIcon()) {
            iconEl = extItem.down(this.iconClsCache, true);
            iconEl.style.backgroundImage = iconSrc ? 'url(' + iconSrc + ')' : '';
        }
    },

    getItemElementConfig: function(index, data) {
        var config = {
                cls: this.itemClsShortCache,
                itemIndex: index,
                children: [{
                    cls: this.labelClsShortCache,
                    html: this.getItemTpl().apply(data)
                }]
            },
            iconSrc;

        if (this.getIcon()) {
            iconSrc = data.iconSrc;
            config.children.push({
                cls: this.iconClsShortCache,
                style: 'background-image: ' + iconSrc ? 'url(' + iconSrc + ')' : ''
            });
        }

        if (this.getDisclosure()) {
            config.children.push({
                cls: this.disclosureClsShortCache + ((data.disclosure === false) ? this.hiddenDisplayCache : '')
            });
        }
        return config;
    },

    findGroupHeaderIndices: function() {
        if (!this.getGrouped()) {
            return;
        }
        var me = this,
            store = me.getStore(),
            groups = store.getGroups(),
            groupLn = groups.length,
            items = me.getViewItems(),
            existingHeaders = me.elementContainer.element.query(me.headerClsCache),
            existingHeadersLn = existingHeaders.length,
            newHeaderItems = [],
            i, firstGroupedRecord, index, item;

        
        for (i = 0; i < existingHeadersLn; i++) {
            Ext.removeNode(existingHeaders[i]);
        }

        
        for (i = 0; i < groupLn; i++) {
            firstGroupedRecord = groups[i].children[0];
            index = store.indexOf(firstGroupedRecord);
            item = items[index];
            me.doAddHeader(item, store.getGroupString(firstGroupedRecord));
            newHeaderItems.push(index);
        }

        return newHeaderItems;
    },

    doAddHeader: function(item, html) {
        Ext.get(item).insertFirst(Ext.Element.create({
            cls: this.headerClsShortCache,
            html: html
        }));
    }
}, function() {
    
    var prototype = this.prototype;

    prototype.cachedConfigList = prototype.cachedConfigList.slice();
    Ext.Array.remove(prototype.cachedConfigList, 'baseCls');
});


Ext.define('Ext.dataview.ListItem', {
    extend: 'Ext.dataview.DataItem',

    xtype: 'listitem',

    requires: [
        'Ext.dataview.ListItemHeader',
        'Ext.dataview.ListDisclosure',
        'Ext.dataview.ListIcon'
    ],

    cachedConfig: {
        dataMap: {
            getIcon: {
                setSrc: 'iconSrc'
            },
            getDisclosure: {
                setVisible: 'disclosure'
            }
        }
    },

    config: {
        baseCls: Ext.baseCSSPrefix + 'list-item',

        header: null,

        icon: null,

        disclosure: null
    },

    applyIcon: function(config) {
        return Ext.factory(config, Ext.dataview.ListIcon, this.getIcon());
    },

    updateIcon: function(newIcon) {
        if (newIcon) {
            this.add(newIcon);
        }
    },

    applyDisclosure: function(config) {
        return Ext.factory(config, Ext.dataview.ListDisclosure, this.getDisclosure());
    },

    updateDisclosure: function(newDisclosure, oldDisclosure) {
        if (newDisclosure) {
            this.add(newDisclosure);
        }
        else if (oldDisclosure) {
            oldDisclosure.destroy();
        }
    },

    applyHeader: function(config) {
        return Ext.factory(config, Ext.dataview.ListItemHeader, this.getHeader());
    },

    updateHeader: function(newHeader, oldHeader) {
        if (newHeader) {
            this.insert(0, newHeader);
        }
        else {
            oldHeader.destroy();
        }
    }
});


Ext.define('Ext.dataview.ComponentList', {
    alternateClassName: 'Ext.ComponentList',
    extend: 'Ext.dataview.ComponentView',
    xtype : 'componentlist',

    requires: [
        'Ext.dataview.ListItem',
        'Ext.dataview.IndexBar'
    ],

    config: {
        
        indexBar: false,

        disclosure: null,
        icon: null,

        
        clearSelectionOnDeactivate: true,

        
        preventSelectionOnDisclose: true,

        
        baseCls: Ext.baseCSSPrefix + 'list',

        
        pinHeaders: true,

        
        defaultType: 'listitem',

        grouped: false,

        innerWidth: 'block',

        itemTpl: null,

        
        onItemDisclosure: null
    },

    constructor: function() {
        this.previousHeaderIndices = [];
        this.callParent(arguments);
    },

    initialize: function() {
        var me = this;
        me.callParent(arguments);
        me.on({
            delegate: '> ' + me.getDefaultType() + ' > listdisclosure',
            tap: 'handleItemDisclosure',
            scope: me
        });
    },

    applyIndexBar: function(indexBar) {
        if (this.getGrouped()) {
            return Ext.factory(indexBar, Ext.dataview.IndexBar, this.getIndexBar());
        }
    },

    updateIndexBar: function(indexBar) {
        if (indexBar && this.getScrollable()) {
            this.getScrollableBehavior().getScrollView().getElement().insertFirst(indexBar.renderElement);

            indexBar.on({
                index: 'onIndex',
                scope: this
            });
        }
    },

    updatePinHeaders: function(pinnedHeaders) {
        var scrollable = this.getScrollable(),
            store = this.getStore(),
            scrollView = this.getScrollableBehavior().getScrollView(),
            scrollViewElement = scrollView.getElement(),
            header, scroller;

        if (scrollable && this.getGrouped()) {
            scroller = scrollable.getScroller();
            if (pinnedHeaders) {
                scroller.on({
                    refresh: 'doRefreshHeaders',
                    scroll: 'onScroll',
                    scope: this
                });

                store.on({
                    datachanged: 'doRefreshHeaders',
                    scope: this
                });

                this.header = header = Ext.create('Ext.dataview.ListItemHeader', {html: ' ', cls: 'x-list-header-swap'});
                scrollViewElement.dom.insertBefore(header.element.dom, scroller.getContainer().dom.nextSibling);
            } else {
                scroller.un({
                    refresh: 'onScrollerRefresh',
                    scroll: 'onScroll',
                    scope: this
                });

                store.un({
                    datachanged: 'doRefreshHeaders',
                    scope: this
                });

                if (this.header) {
                    this.header.destroy();
                }
            }
        }
    },

    
    getClosestGroups : function() {
        var groups = this.pinHeaderInfo.offsets,
            pos = this.getScrollable().getScroller().position,
            ln = groups.length,
            group, i,
            current, next;

        for (i = 0; i < ln; i++) {
            group = groups[i];
            if (group.offset > pos.y) {
                next = group;
                break;
            }
            current = group;
        }

        return {
            current: current,
            next: next
        };
    },

    doRefreshHeaders: function() {
        var headerIndicis = this.previousHeaderIndices,
            ln = headerIndicis.length,
            items = this.getViewItems(),
            headerInfo = this.pinHeaderInfo = {offsets: []},
            headerOffsets = headerInfo.offsets,
            i, headerItem, header;

        if (ln) {
            for (i = 0; i < ln; i++) {
                headerItem = items[headerIndicis[i].index];
                header = this.getItemHeader(headerItem);

                headerOffsets.push({
                    header: header,
                    offset: headerItem.element.dom.offsetTop
                });

                header.element.setVisibilityMode(Ext.Element.VISIBILITY);
            }

            headerInfo.headerHeight = header.element.getHeight();
            headerInfo.closest = this.getClosestGroups();
            this.setActiveGroup(headerInfo.closest.current);
        }
    },

    getItemHeader: function(item) {
        return item.getHeader();
    },

    onScroll: function(scroller, x, y) {
        var me = this,
            headerInfo = me.pinHeaderInfo,
            closest = headerInfo.closest,
            activeGroup = me.activeGroup,
            headerHeight = headerInfo.headerHeight,
            next = closest.next,
            current = closest.current,
            header = this.header;

        if (y <= 0) {
            if (activeGroup) {
                me.setActiveGroup(false);
                closest.next = current;
            }
            return;
        }
        else if (
            (next && y > next.offset) ||
            (y < current.offset)
        ) {
            closest = headerInfo.closest = this.getClosestGroups();
            next = closest.next;
            current = closest.current;
            this.setActiveGroup(current);
        }

        if (next && y > 0 && next.offset - y <= headerHeight) {
            var transform = headerHeight - (next.offset - y)
            
            header.element.dom.style.webkitTransform = 'translate3d(0px, -' + transform + 'px, 0px)';
            this.transformed = true;
        }
        else if (this.transformed) {
            header.element.dom.style.webkitTransform = null;
            this.transformed = false;
        }
    },

    
    setActiveGroup : function(group) {
        var me = this;
        if (group) {
            if (!me.activeGroup || me.activeGroup.header != group.header) {
                me.header.setHtml(group.header.getHtml());
                me.header.show();
            }
        } else {
            me.header.hide();
        }

        this.activeGroup = group;
    },

    onIndex: function(index) {
        var key = index.toLowerCase(),
            store = this.getStore(),
            groups = store.getGroups(),
            ln = groups.length,
            group, i, closest, id, item;

        for (i = 0; i < ln; i++) {
            group = groups[i];
            id = group.name.toLowerCase();
            if (id == key || id > key) {
                closest = group;
                break;
            }
            else {
                closest = group;
            }
        }

        item = this.getViewItems()[store.indexOf(closest.children[0])];
        this.getScrollable().getScroller().scrollTo(0, item.element.dom.offsetTop);
    },

    applyOnItemDisclosure: function(config) {
        if (Ext.isFunction(config)) {
            return {
                scope: this,
                handler: config
            };
        }
        if (Ext.isObject(config)) {
            return config;
        }
        return null;
    },

    getDisclosure: function() {
        var value = this._disclosure,
            onItemDisclosure = this.getOnItemDisclosure();

        if (onItemDisclosure && onItemDisclosure != value) {
            value = true;
            this.setDisclosure(value);
        }

        return value;
    },

    updateOnItemDisclosure: function(newOnItemDisclosure) {
        
        if (newOnItemDisclosure) {
            this.setDisclosure(true);
        }
    },

    handleItemDisclosure: function(disclosure, e) {
        var me = this,
            listItem = disclosure.ownerCt,
            index = me.getViewItems().indexOf(listItem),
            record = me.getStore().getAt(index),
            onItemDisclosure = me.getOnItemDisclosure();
        if (me.getPreventSelectionOnDisclose()) {
            e.stopEvent();
        }
        me.fireAction('disclose', [record, listItem, index, e], 'doDisclose');

        if (onItemDisclosure && onItemDisclosure.handler) {
            onItemDisclosure.handler.call(me, record, listItem, index);
        }
    },

    doDisclose: Ext.emptyFn,

    updateItemTpl: function(newTpl) {
        this.getItemConfig().tpl = newTpl;
    },

    getItemConfig: function() {
        var me = this,
            config, tpl;
        if (!me._isItemConfigInitialized) {
            this_isItemConfigInitialized = true;
            me.setItemConfig(me.config.itemConfig);
        }
        config = me._itemConfig;
        tpl = me.getItemTpl();
        if (tpl) {
            config.tpl = tpl;
        }
        return config;
    },

    getDataItemConfig: function(xtype, record, itemConfig) {
        return {
            xtype: xtype,
            record: record,
            defaults: itemConfig,
            disclosure: this.getDisclosure(),
            icon: this.getIcon()
        };
    },

    findGroupHeaderIndices: function() {
        if (!this.getGrouped()) {
            return;
        }
        var me = this,
            store = me.getStore(),
            groups = store.getGroups(),
            groupLn = groups.length,
            items = me.getViewItems(),
            i = 0,
            previousHeaderIndices = me.previousHeaderIndices,
            previousIndexLn = previousHeaderIndices.length,
            newHeaderIndices = [],
            firstGroupedRecord, index, oldItemWithHeader;

        
        for (; i < groupLn; i++) {
            firstGroupedRecord = groups[i].children[0];
            index = store.indexOf(firstGroupedRecord);
            if (previousHeaderIndices.indexOf(firstGroupedRecord) == -1) {
                me.doAddHeader(items[index], store.getGroupString(firstGroupedRecord));
            }
            newHeaderIndices.push(firstGroupedRecord);
        }

        
        for (i = 0; i < previousIndexLn; i++) {
            oldItemWithHeader = previousHeaderIndices[i];
            if (newHeaderIndices.indexOf(oldItemWithHeader) == -1) {
                oldItemWithHeader = items[store.indexOf(oldItemWithHeader)];
                if (oldItemWithHeader) {
                    me.doRemoveHeader(oldItemWithHeader);
                }
            }
        }

        me.previousHeaderIndices = newHeaderIndices;
    },

    doAddHeader: function(item, html) {
        item.setHeader({
            html: html
        });
    },

    doRemoveHeader: function(item) {
        item.setHeader(null);
    },

    doRefresh: function() {
        this.callParent(arguments);
        this.findGroupHeaderIndices();
    },

    onStoreAdd: function() {
        this.callParent(arguments);
        this.findGroupHeaderIndices();
    },
    onStoreRemove: function() {
        this.callParent(arguments);
        this.findGroupHeaderIndices();
    },
    onStoreUpdate: function() {
        this.callParent(arguments);
        this.findGroupHeaderIndices();
    }
});


Ext.define('Ext.dataview.NestedList', {
    alternateClassName: 'Ext.NestedList',
    extend: 'Ext.Container',
    xtype : 'nestedlist',
    requires: [
        'Ext.List',
        'Ext.Toolbar',
        'Ext.Button',
        'Ext.XTemplate',
        'Ext.data.StoreManager',
        'Ext.data.NodeStore',
        'Ext.data.TreeStore'
    ],

    config: {
        
        cls: Ext.baseCSSPrefix + 'nested-list',

        
        cardSwitchAnimation: 'slide',

        
        backText: 'Back',

        
        useTitleAsBackText: true,

        
        updateTitleText: true,

        
        displayField: 'text',

        
        loadingText: 'Loading...',

        
        emptyText: 'No items available.',

        
        onItemDisclosure: false,

        
        allowDeselect: false,

        
        useToolbar: null,

        
        toolbar: {
            docked: 'top',
            xtype: 'navigationbar',
            ui: 'light',
            inline: true
        },

        
        title: '',

        
        layout: {
            type: 'card',
            animation: {
                type: 'slide',
                duration: 250,
                direction: 'left'
            }
        },

        
        data: null,

        
        store: null,

        
        detailContainer: undefined,

        
        detailCard: null,

        
        backButton: {
            ui: 'back',
            hidden: true
        },

        lastNode: null,

        lastActiveList: null,

        pressedDelay: 0
    },

    

    

    

    

    

    

    

    

    

        //@private

    initialize: function() {
        var me = this;
        me.callParent(arguments);

        me.on({
            delegate: '> list',
            itemdoubletap: 'onItemDoubleTap',
            itemtap: 'onItemTap',
            scope: me
        });
    },

    applyDetailContainer: function(config) {
        if (!config) {
            config = this;
        }

        return config;
    },

    
    onItemTap: function(list, index, item, e) {
        var me = this,
            store = list.getStore(),
            node = store.getAt(index);

        me.fireAction('itemtap', [list, index, item, e], 'doItemTap');
        if (node.isLeaf()) {
            me.fireAction('leafitemtap', [list, index, item, e], 'doLeafItemTap');
            me.goToLeaf(node);
        }
        else {
            this.goToNode(node);
        }
    },

    doItemTap: Ext.emptyFn,

    doLeafItemTap: Ext.emptyFn,

    
    onItemDoubleTap: function(list, index, item, e) {
        this.fireAction('itemdoubletap', [list, index, item, e], 'doItemDoubleTap');
    },

    doItemDoubleTap: Ext.emptyFn,

    
    onBackTap: function() {
        var me = this,
            node = me.getLastNode(),
            detailCard = me.getDetailCard(),
            detailCardActive = detailCard && me.getActiveItem() == detailCard,
            lastActiveList = me.getLastActiveList();

        this.fireAction('back', [this, node, lastActiveList, detailCardActive], 'doBack');
    },

    doBack: function(me, node, lastActiveList, detailCardActive) {
        if (detailCardActive && lastActiveList) {
            me.getLayout().getAnimation().setReverse(true);
            me.setActiveItem(lastActiveList);
            me.setLastNode(node.parentNode);
            me.syncToolbar();
        }
        else {
            this.goToNode(node.parentNode);
        }
    },

    updateData: function(data) {
        if (!this.getStore()) {
            this.setStore(new Ext.data.TreeStore({
                root: data
            }));
        }
    },

    applyStore: function(store) {
        if (store) {
            store = Ext.data.StoreManager.lookup(store);
        }

        return store;
    },

    updateStore: function(newStore, oldStore) {
        var me = this,
            rootNode;
        if (oldStore && Ext.isObject(oldStore) && oldStore.isStore) {
            if (oldStore.autoDestroy) {
                oldStore.destroy();
            }
            else {
                oldStore.un({
                    rootchange: 'goToNode',
                    scope: me
                });
            }
        }
        if (newStore) {
            rootNode = newStore.getRootNode();
            if (rootNode) {
                me.goToNode(rootNode);
            }
            else {
                newStore.on({
                    load: 'onLoad',
                    single: true,
                    scope: this
                });
                newStore.load();
            }
            newStore.on({
                rootchange: 'goToNode',
                scope: this
            });
            me.relayEvents(newStore, [
                'beforeload',
                'load'
            ]);
        }
    },

    onLoad: function(store) {
        this.goToNode(store.getRootNode());
    },

    applyBackButton: function(config) {
        return Ext.factory(config, Ext.Button, this.getBackButton());
    },

    applyDetailCard: function(config) {
        return this.factoryItem(config);
    },

    updateBackButton: function(newButton, oldButton) {
        if (newButton) {
            var me = this;
            newButton.on('tap', me.onBackTap, me);
            newButton.setText(me.getBackText());
            me.getToolbar().add(0, newButton);
        }
        else if (oldButton) {
            oldButton.destroy();
        }
    },

    applyToolbar: function(config) {
        return Ext.factory(config, Ext.NavigationBar, this.getToolbar());
    },

    updateToolbar: function(newToolbar, oldToolbar) {
        var me = this;
        if (newToolbar) {
            newToolbar.setTitle(me.getTitle());
            if (!newToolbar.getParent()) {
                me.add(newToolbar);
            }
        }
        else if (oldToolbar) {
            oldToolbar.destroy();
        }
    },

    setUseToolbar: function(config) {
        Ext.Logger.deprecate("The 'useToolbar' config is deprecated, use the 'toolbar' config instead", this);
    },

    updateTitle: function(newTitle) {
        var me = this,
            toolbar = me.getToolbar();
        if (toolbar) {
            if (me.getUpdateTitleText()) {
                toolbar.setTitle(newTitle);
            }
        }
    },

    
    getItemTextTpl: function(node) {
        return '{' + this.getDisplayField() + '}';
    },

    
    getTitleTextTpl: function(node) {
        return '{' + this.getDisplayField() + '}';
    },

    
    renderTitleText: function(node, forBackButton) {
        if (!node.titleTpl) {
            node.titleTpl = Ext.create('Ext.XTemplate', this.getTitleTextTpl(node));
        }

        if (node.isRoot()) {
            var initialTitle = this.getInitialConfig('title');
            return (forBackButton && initialTitle === '') ? this.getInitialConfig('backText') : initialTitle;
        }

        return  node.titleTpl.applyTemplate(node.data);
    },

    
    goToNode: function(node) {
        if (!node) {
            return;
        }

        var me = this,
            activeItem = me.getActiveItem(),
            detailCard = me.getDetailCard(),
            detailCardActive = detailCard && me.getActiveItem() == detailCard,
            reverse = me.goToNodeReverseAnimation(node),
            firstList = me.firstList,
            secondList = me.secondList,
            animation = me.getLayout().getAnimation(),
            list;

        
        if (node.isLeaf()) {
            throw new Error('goToNode: passed a node which is a leaf.');
        }

        
        if (node == me.getLastNode() && !detailCardActive) {
            return;
        }

        if (detailCardActive) {
            animation.setReverse(true);
            me.setActiveItem(me.getLastActiveList());
        }
        else {
            if (firstList && secondList) {
                
                activeItem = me.getActiveItem();

                me.setLastActiveList(activeItem);
                list = (activeItem == firstList) ? secondList : firstList;
                list.getStore().setNode(node);

                animation.setReverse(reverse);
                me.setActiveItem(list);
                list.deselectAll();
            }
            else if (firstList) {
                
                me.setLastActiveList(me.getActiveItem());
                me.setActiveItem(me.getListConfig(node));
                me.secondList = me.getActiveItem();
            }
            else {
                
                me.setActiveItem(me.getListConfig(node));
                me.firstList = me.getActiveItem();
            }
        }

        me.fireEvent('listchange', this, me.getActiveItem());

        me.setLastNode(node);

        me.syncToolbar();
    },

    
    goToLeaf: function(node) {
        if (!node.isLeaf()) {
            throw new Error('goToLeaf: passed a node which is not a leaf.');
        }

        var me = this,
            card = me.getDetailCard(node),
            container = me.getDetailContainer(),
            sharedContainer = container == this,
            animation = me.getLayout().getAnimation();

        if (card) {
            if (container.getItems().indexOf(card) === -1) {
                container.add(card);
            }
            if (sharedContainer) {
                me.setLastActiveList(me.getActiveItem());
                me.setLastNode(node);
            }
            animation.setReverse(false);
            container.setActiveItem(card);
            me.syncToolbar();
        }
    },

    
    syncToolbar: function(forceDetail) {
        var me = this,
            detailCard = me.getDetailCard(),
            node = me.getLastNode(),
            detailActive = forceDetail || (detailCard && (me.getActiveItem() == detailCard)),
            parentNode = (detailActive) ? node : node.parentNode,
            backButton = me.getBackButton();

        
        if (backButton) {
            backButton[parentNode ? 'show' : 'hide']();
            if (parentNode && me.getUseTitleAsBackText()) {
                backButton.setText(me.renderTitleText(node.parentNode, true));
            }
        }

        if (node) {
            me.setTitle(me.renderTitleText(node));
        }
    },

    updateBackText: function(newText) {
        this.getBackButton().setText(newText);
    },

    
    goToNodeReverseAnimation: function(node) {
        var me = this,
            lastNode = me.getLastNode();
        if (!lastNode) {
            return false;
        }

        return (!lastNode.contains(node) && lastNode.isAncestor(node)) ? true : false;
    },

    
    getListConfig: function(node) {
        var me = this,
            nodeStore = Ext.create('Ext.data.NodeStore', {
                recursive: false,
                node: node,
                model: me.getStore().model
            });

        return {
            xtype: 'list',
            autoDestroy: true,
            clearSelectionOnDeactivate: false,
            disclosure: false,
            store: nodeStore,
            onItemDisclosure: me.getOnItemDisclosure(),
            allowDeselect : me.getAllowDeselect(),
            itemTpl: '<span<tpl if="leaf == true"> class="x-list-item-leaf"</tpl>>' + me.getItemTextTpl(node) + '</span>'
        };
    }
});


Ext.define('Ext.form.FieldSet', {
    extend  : 'Ext.Container',
    alias   : 'widget.fieldset',
    requires: ['Ext.Title'],

    config: {
        
        baseCls: Ext.baseCSSPrefix + 'form-fieldset',

        
        title: null,

        
        instructions: null,

        
        layout: {
            type : 'vbox',
            align: 'stretch'
        }
    },

    
    applyTitle: function(title) {
        if (typeof title == 'string') {
            title = {title: title};
        }

        Ext.applyIf(title, {
            docked : 'top',
            baseCls: this.getBaseCls() + '-title'
        });

        return Ext.factory(title, Ext.Title, this.getTitle());
    },

    
    updateTitle: function(newTitle, oldTitle) {
        if (newTitle) {
            this.add(newTitle);
        }
        if (oldTitle) {
            this.remove(oldTitle);
        }
    },

    
    applyInstructions: function(instructions) {
        if (typeof instructions == 'string') {
            instructions = {title: instructions};
        }

        Ext.applyIf(instructions, {
            docked : 'bottom',
            baseCls: this.getBaseCls() + '-instructions'
        });

        return Ext.factory(instructions, Ext.Title, this.getInstructions());
    },

    
    updateInstructions: function(newInstructions, oldInstructions) {
        if (newInstructions) {
            this.add(newInstructions);
        }
        if (oldInstructions) {
            this.remove(oldInstructions);
        }
    }
});


Ext.define('Ext.form.Panel', {
    alternateClassName: 'Ext.form.FormPanel',
    extend  : 'Ext.Panel',
    xtype   : 'formpanel',
    requires: ['Ext.XTemplate', 'Ext.field.Checkbox', 'Ext.Ajax'],

    

    

    

    config: {
        
        cls: Ext.baseCSSPrefix + 'form',

        
        standardSubmit: false,

        
        url: null,

        
        elConfig: { tag: 'form' },

        
        baseParams : null,

        
        waitTpl: '<div class="{cls}">{message}&hellip;</div>',

        
        submitOnAction : true,

        
        maskTarget: null,

        
        record: null,

        
        layout: {
            type : 'vbox',
            align: 'stretch'
        },

        
        scrollable: {
            scrollMethod: 'scrollposition'
        }
    },

    
    initialize: function() {
        var me = this;

        me.on({
            action: 'onFieldAction',
            scope : me
        });

        me.element.on({
            submit: 'onSubmit',
            scope : this
        });

        me.callParent(arguments);
    },

    
    applyWaitTpl: function(waitTpl) {
        if (waitTpl) {
            if (Ext.isArray(waitTpl) || typeof waitTpl === "string") {
                waitTpl = Ext.create('Ext.XTemplate', waitTpl);
            }
        }
        return waitTpl;
    },

    
    setRecord: function(record) {
        var me = this;

        if (record && record.data) {
            me.setValues(record.data);
        }

        me._record = record;

        return this;
    },

    
    onSubmit: function(e) {
        var me = this;
        if (!me.getStandardSubmit() || me.fireAction('submit', [me, me.getValues(true)], 'doSubmit') === false) {
            if (e) {
                e.stopEvent();
            }
        }
    },

    doSubmit: Ext.emptyFn,

    
    onFieldAction: function(field) {
        if (this.getSubmitOnAction()) {
            field.blur();
            this.submit();
        }
    },

    
    submit: function(options) {
        var me = this,
            form = me.element.dom || {},
            formValues;

        options = Ext.apply({
            url : me.getUrl() || form.action,
            submit: false,
            method : form.method || 'post',
            autoAbort : false,
            params : null,
            waitMsg : null,
            headers : null,
            success : null,
            failure : null
        }, options || {});

        formValues = me.getValues(me.getStandardSubmit() || !options.submitDisabled);

        if (me.getStandardSubmit()) {
            if (options.url && Ext.isEmpty(form.action)) {
                form.action = options.url;
            }

            form.method = (options.method || form.method).toLowerCase();

            if (me.fireEvent('beforesubmit', me, formValues, options) !== false) {
                form.submit();
            }
        }
        else {
            if (me.fireEvent('beforesubmit', me, formValues, options) !== false) {
                if (options.waitMsg) {
                    me.showMask(options.waitMsg);
                }

                return Ext.Ajax.request({
                    url: options.url,
                    method: options.method,
                    rawData: Ext.urlEncode(Ext.apply(
                        Ext.apply({}, me.getBaseParams() || {}),
                        options.params || {},
                        formValues
                    )),
                    autoAbort: options.autoAbort,
                    headers: Ext.apply(
                        {'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'},
                        options.headers || {}),
                    scope    : me,
                    callback: function(callbackOptions, success, response) {
                        var me = this,
                            responseText = response.responseText,
                            failureFn;

                        me.hideMask();

                        failureFn = function() {
                            if (Ext.isFunction(options.failure)) {
                                options.failure.call(options.scope || me, me, response, responseText);
                            }
                            me.fireEvent('exception', me, response);
                        };

                        if (success) {
                            response = Ext.decode(responseText);
                            success = !!response.success;
                            if (success) {
                                if (Ext.isFunction(options.success)) {
                                    options.success.call(options.scope || me, me, response, responseText);
                                }
                                me.fireEvent('submit', me, response);
                            } else {
                                failureFn();
                            }
                        }
                        else {
                            failureFn();
                        }
                    }
                });
            }
        }
    },

    
    updateRecord: function(instance, enabled) {
        var fields, values, name;

        if (instance && (fields = instance.fields)) {
            values = this.getValues(enabled);
            for (name in values) {
                if (values.hasOwnProperty(name) && fields.containsKey(name)) {
                    instance.set(name, values[name]);
                }
            }
        }
        return this;
    },

    
    setValues: function(values) {
        var fields = this.getFields(),
            name, field, value;

        values = values || {};

        for (name in values) {
            if (values.hasOwnProperty(name)) {
                field = fields[name];
                value = values[name];
                if (field) {
                    if (Ext.isArray(field)) {
                        field.forEach(function(f) {
                            if (f.isRadio) {
                                f.setGroupValue(value);
                            } else if (Ext.isArray(values[name])) {
                                f.setChecked((value.indexOf(f.getValue()) != -1));
                            } else {
                                f.setChecked((value == f.getValue()));
                            }
                        });
                    } else {
                        if (field.setChecked) {
                            field.setChecked(value);
                        } else {
                            field.setValue(value);
                        }
                    }
                }
            }
        }

        return this;
    },

    
    getValues: function(enabled) {
        var fields = this.getFields(),
            values = {},
            field, name, ln, i;

        for (name in fields) {
            if (fields.hasOwnProperty(name)) {
                if (Ext.isArray(fields[name])) {
                    values[name] = [];

                    ln = fields[name].length;

                    for (i = 0; i < ln; i++) {
                        field = fields[name][i];

                        if (!field.getChecked) {
                            values[name] = field.getValue();

                            throw new Error("Ext.form.Panel: [getValues] You have multiple fields with the same 'name' configuration of '" + name + "' in your form panel (#" + this.id + ").");

                            break;
                        }

                        if (!(enabled && field.getDisabled())) {
                            if (field.isRadio) {
                                values[name] = field.getGroupValue();
                            } else {
                                values[name].push(field.getValue());
                            }
                        }

                        
                    }
                } else {
                    field = fields[name];

                    if (!(enabled && field.getDisabled())) {
                        if (field.isCheckbox) {
                            values[name] = (field.getChecked()) ? field.getValue() : null;
                        } else {
                            values[name] = field.getValue();
                        }
                    }
                }
            }
        }

        return values;
    },

    
    reset: function() {
        this.getFieldsAsArray().forEach(function(field) {
            field.reset();
        });

        return this;
    },

    
    enable: function() {
        this.getFieldsAsArray().forEach(function(field) {
            field.enable();
        });

        return this;
    },

    
    disable: function() {
        this.getFieldsAsArray().forEach(function(field) {
            field.disable();
        });

        return this;
    },

    getFieldsAsArray: function() {
        var fields = [],
            getFieldsFrom = function(item) {
                if (item.isField) {
                    fields.push(item);
                }

                if (item.isContainer) {
                    item.getItems().each(getFieldsFrom);
                }
            };

        this.getItems().each(getFieldsFrom);

        return fields;
    },

    
    getFields: function(byName) {
        var fields = {},
            itemName;

        var getFieldsFrom = function(item) {
            if (item.isField) {
                itemName = item.getName();

                if ((byName && itemName == byName) || typeof byName == 'undefined') {
                    if (fields.hasOwnProperty(itemName)) {
                        if (!Ext.isArray(fields[itemName])) {
                            fields[itemName] = [fields[itemName]];
                        }

                        fields[itemName].push(item);
                    } else {
                        fields[itemName] = item;
                    }
                }

            }

            if (item.isContainer) {
                item.items.each(getFieldsFrom);
            }
        };

        this.items.each(getFieldsFrom);

        return (byName) ? (fields[byName] || []) : fields;
    },

    getFieldsFromItem: Ext.emptyFn,

    
    showMask: function(cfg, target) {
        cfg = Ext.isString(cfg) ? {message : cfg} : cfg;
        var me = this,
            waitTpl = me.getWaitTpl();

        if (cfg && waitTpl) {
            target = Ext.get(target || cfg.target) || me.getEl();
            me.setMaskTarget(target);
            if (target) {
                target.mask(waitTpl.apply(cfg));
            }
        }
        return me;
    },

    
    hideMask: function() {
        var me = this,
            maskTarget = me.getMaskTarget();
        
        if (maskTarget) {
            maskTarget.unmask();
            me.setMaskTarget(null);
        }
        return me;
    }
}, function() {
    this.override({
        
        loadRecord: function(instance) {
            return this.setRecord.apply(this, arguments);
        },

        
        loadModel: function() {
            return this.setRecord.apply(this, arguments);
        },

        constructor: function(config) {
            
            if (config && config.hasOwnProperty('waitMsgTarget')) {
                config.maskTarget = config.waitMsgTarget;
                delete config.waitMsgTarget;
            }

            this.callParent([config]);
        }
    });

    
    Ext.form.Panel.prototype.load = Ext.form.Panel.prototype.loadModel;
});


Ext.define('Ext.picker.Slot', {
    extend: 'Ext.DataView',
    xtype : 'pickerslot',
    alternateClassName: 'Ext.Picker.Slot',
    requires: [
        'Ext.XTemplate',
        'Ext.data.Store',
        'Ext.Component',
        'Ext.data.StoreManager'
    ],

    isSlot: true,

    config: {
        
        title: null,

        
        showTitle: true,

        
        cls: Ext.baseCSSPrefix + 'picker-slot',

        
        name: null,

        
        value: null,

        
        flex: 1,

        
        align: 'left',

        
        itemSelector: 'div.' + Ext.baseCSSPrefix + 'picker-item',

        
        displayField: 'text',

        
        valueField: 'value',

        
        scrollable: {
            direction : 'vertical',
            indicators: false
        }
    },

    
    selectedIndex: 0,

    
    applyTitle: function(title) {
        
        if (title) {
            
            title = Ext.create('Ext.Component', {
                cls: Ext.baseCSSPrefix + 'picker-slot-title',
                docked      : 'top',
                html        : title
            });
        }

        return title;
    },

    updateTitle: function(newTitle, oldTitle) {
        if (newTitle) {
            this.add(newTitle);
            this.setupBar();
        }

        if (oldTitle) {
            this.remove(oldTitle);
        }
    },

    updateShowTitle: function(showTitle) {
        var title = this.getTitle();
        if (title) {
            title[showTitle ? 'show' : 'hide']();

            this.setupBar();
        }
    },

    updateDisplayField: function(newDisplayField) {
        this.setItemTpl('<div class="' + Ext.baseCSSPrefix + 'picker-item {cls} <tpl if="extra">' + Ext.baseCSSPrefix + 'picker-invalid</tpl>">{' + newDisplayField + '}</div>');
    },

    
    updateAlign: function(newAlign, oldAlign) {
        var element = this.element;
        element.addCls(Ext.baseCSSPrefix + 'picker-' + newAlign);
        element.removeCls(Ext.baseCSSPrefix + 'picker-' + oldAlign);
    },

    
    applyData: function(data) {
        var parsedData = [],
            ln = data && data.length,
            i, item, obj;

        if (data && Ext.isArray(data) && ln) {
            for (i = 0; i < ln; i++) {
                item = data[i];
                obj = {};
                if (Ext.isArray(item)) {
                    obj[this.valueField] = item[0];
                    obj[this.displayField] = item[1];
                }
                else if (Ext.isString(item)) {
                    obj[this.valueField] = item;
                    obj[this.displayField] = item;
                }
                else if (Ext.isObject(item)) {
                    obj = item;
                }
                parsedData.push(obj);
            }
        }

        return data;
    },

    updateData: function(data) {
        this.setStore(Ext.create('Ext.data.Store', {
            model: 'x-textvalue',
            data : data
        }));
    },

    
    initialize: function() {
        var me = this,
            scroller = this.getScrollable().getScroller();

        me.callParent(arguments);

        me.on({
            scope: this,
            painted: 'onPainted'
        });

        scroller.on({
            scope: this,

            scrollend: 'onScrollEnd'
        });
    },

    
    onPainted: function() {
        this.setupBar();
    },

    
    setupBar: function() {
        if (!this.rendered) {
            
            return;
        }

        var element = this.element,
            innerElement = this.innerElement,
            picker = this.picker,
            bar = picker.bar,
            value = this.getValue(),
            showTitle = this.getShowTitle(),
            title = this.getTitle(),
            scrollable = this.getScrollable(),
            scroller = scrollable.getScroller(),
            barY, elY, barHeight, padding, titleHeight, paddingBottom;

        barY = bar.getY();
        elY = element.getY();

        if (showTitle && title) {
            elY += title.element.getHeight();
        }

        padding = paddingBottom = Math.abs(elY - barY);
        this.slotPadding = padding;

        if (showTitle && title) {
            titleHeight = title.element.getHeight();
            paddingBottom += titleHeight;
        }

        innerElement.setStyle({
            padding: padding + 'px 0 ' + paddingBottom + 'px'
        });

        barHeight = bar.getHeight();
        scroller.refresh();
        scroller.setSnap(barHeight);

        this.setValue(value);
    },

    
    doItemTap: function(list, index, item, e) {
        this.selectedIndex = index;
        this.selectedNode = item;
        this.scrollToItem(item, true);

        this.fireAction('slotpick', [this.getValue(), this.selectedNode]);
    },

    
    scrollToItem: function(item, animated) {
        var y = item.getY(),
            parentEl = item.parent(),
            parentY = parentEl.getY(),
            
            scrollView = this.getScrollable(),
            scroller = scrollView.getScroller(),
            difference;

        difference = y - parentY;
        if (animated) {
            scroller.scrollToAnimated(0, difference);
        } else {
            scroller.scrollTo(0, difference);
        }
    },

    
    onScrollEnd: function(scroller, position) {
        var picker = this.picker,
            bar = picker.bar,
            barHeight = bar.getHeight(),
            offset = position.y,
            index = Math.round(offset / barHeight),
            viewItems = this.getViewItems(),
            item = viewItems[index];

        if (item) {
            this.selectedIndex = index;
            this.selectedNode = item;

            this.fireAction('slotpick', [this.getValue(), this.selectedNode]);
        }
    },

    
    getValue: function() {
        var store = this.getStore(),
            record, value;

        if (!store) {
            return;
        }

        record = store.getAt(this.selectedIndex);

        value = record ? record.get(this.getValueField()) : null;
        this._value = value;

        return value;
    },

    
    setValue: function(value) {
        if (!value) {
            return;
        }

        if (!this.rendered) {
            
            this._value = value;
            return;
        }

        var store = this.getStore(),
            viewItems = this.getViewItems(),
            valueField = this.getValueField(),
            index, item;

        index = store.find(valueField, value);
        if (index != -1) {
            item = Ext.get(viewItems[index]);

            this.selectedIndex = index;
            this.scrollToItem(item);

            this._value = value;
        }
    },

    
    setValueAnimated: function(value) {
        if (!value) {
            return;
        }

        if (!this.rendered) {
            
            this._value = value;
            return;
        }

        var store = this.getStore(),
            viewItems = this.getViewItems(),
            valueField = this.getValueField(),
            index, item;

        index = store.find(valueField, value);
        if (index != -1) {
            item = Ext.get(viewItems[index]);

            this.selectedIndex = index;
            this.scrollToItem(item, true);

            this._value = value;
        }
    }
});


Ext.define('Ext.picker.Picker', {
    extend: 'Ext.Sheet',
    alias : 'widget.picker',
    alternateClassName: 'Ext.Picker',
    requires: ['Ext.picker.Slot', 'Ext.Toolbar', 'Ext.data.Model'],

    

    

    

    config: {
        
        cls: Ext.baseCSSPrefix + 'picker',

        
        doneButton: 'Done',

        
        cancelButton: 'Cancel',

        
        useTitles: true,

        
        slots: null,

        
        value: null,

        
        height: 220,

        
        layout: {
            type : 'hbox',
            align: 'stretch'
        },

        
        centered: false,

        
        left : 0,

        
        right: 0,

        
        bottom: 0,

        
        defaultType: 'pickerslot',

        
        toolbar: true
    },

    initElement: function() {
        this.callParent(arguments);

        var me = this,
            clsPrefix = Ext.baseCSSPrefix,
            innerElement = this.innerElement;

        
        this.mask = innerElement.createChild({
            cls: clsPrefix + 'picker-mask'
        });

        this.bar = this.mask.createChild({
            cls: clsPrefix + 'picker-bar'
        });

        me.on({
            scope   : this,
            delegate: 'pickerslot',

            slotpick    : 'onSlotPick'
        });

        me.on({
            scope: this,

            show: 'onShow'
        });
    },

    
    applyToolbar: function(config) {
        if (config === true) {
            config = {};
        }

        Ext.applyIf(config, {
            docked: 'top'
        });

        return Ext.factory(config, 'Ext.Toolbar', this.getToolbar());
    },

    
    updateToolbar: function(newToolbar, oldToolbar) {
        if (newToolbar) {
            this.add(newToolbar);
        }

        if (oldToolbar) {
            this.remove(oldToolbar);
        }
    },

    
    applyDoneButton: function(config) {
        if (typeof config == "string") {
            config = {
                text: config
            };
        }

        Ext.applyIf(config, {
            ui: 'action'
        });

        return Ext.factory(config, 'Ext.Button', this.getDoneButton());
    },

    updateDoneButton: function(newDoneButton, oldDoneButton) {
        var toolbar = this.getToolbar(),
            
            //@todo remove this when toolbarlayout is fixed

            cancelButton = this.getCancelButton();
        
        if (newDoneButton) {
            toolbar.add([
                { xtype: 'spacer' },
                newDoneButton
            ]);
            newDoneButton.on('tap', this.onDoneButtonTap, this);
        } else if (oldDoneButton) {
            toolbar.remove(oldDoneButton);
        }
    },

    
    applyCancelButton: function(config) {
        if (typeof config == "string") {
            config = {
                text: config
            };
        }

        return Ext.factory(config, 'Ext.Button', this.getCancelButton());
    },

    updateCancelButton: function(newCancelButton, oldCancelButton) {
        var toolbar = this.getToolbar();

        if (newCancelButton) {
            toolbar.add(newCancelButton);
            newCancelButton.on('tap', this.onCancelButtonTap, this);
        } else if (oldCancelButton) {
            toolbar.remove(oldCancelButton);
        }
    },

    
    updateUseTitles: function(useTitles) {
        var innerItems = this.getInnerItems(),
            ln = innerItems.length,
            i;

        for (i = 0; i < ln; i++) {
            innerItems[i].setShowTitle(useTitles);
        }
    },

    applySlots: function(slots) {
        
        if (slots) {
            var ln = slots.length,
                i;

            for (i = 0; i < ln; i++) {
                slots[i].picker = this;
            }
        }

        return slots;
    },

    
    updateSlots: function(newSlots, oldSlots) {
        if (oldSlots) {
            this.removeAll();
        }

        if (newSlots) {
            this.add(newSlots);
        }

        this.updateUseTitles(this.getUseTitles());
    },

    
    onDoneButtonTap: function() {
        
        
        
        
        
        
        
        this.fireAction('change', [this, this.getValue()]);
        this.hide();
    },

    
    onCancelButtonTap: function() {
        
        
        
        
        
        
        
        
        
        this.fireEvent('cancel', this);
        this.hide();
    },

    
    onSlotPick: function(slot, value, node) {
        this.fireAction('pick', [this, this.getValue(), slot]);
    },

    onShow: function() {
        if (!this.isHidden()) {
            this.setValue(this._value);
        }
    },

    
    setValue: function(values, animated) {
        var slot,
            me = this,
            items = me.items.items,
            ln = items.length;

        
        if (!values) {
            return this;
        }

        if (this.rendered && !this.isHidden()) {
            Ext.iterate(values, function(key, value) {
                slot = me.child('[_name=' + key + ']');

                if (slot) {
                    if (animated) {
                        slot.setValueAnimated(value);
                    } else {
                        slot.setValue(value);
                    }
                }
            }, this);
        }

        me._value = values;
        me._values = values;

        return this;
    },

    setValueAnimated: function(values) {
        this.setValue(values, true);
    },

    
    getValue: function() {
        var values = {},
            items = this.getItems().items,
            ln = items.length,
            item, i;

        for (i = 0; i < ln; i++) {
            item = items[i];
            if (item instanceof Ext.picker.Slot) {
                values[item.getName()] = item.getValue();
            }
        }

        this._values = values;

        return values;
    },

    
    getValues: function() {
        return this.getValue();
    }
}, function() {
    Ext.define('x-textvalue', {
        extend: 'Ext.data.Model',
        fields: ['text', 'value']
    });
});


Ext.define('Ext.field.Select', {
    extend: 'Ext.field.Text',
    alias : 'widget.selectfield',
    alternateClassName: 'Ext.form.Select',
    requires: [
        'Ext.Panel',
        'Ext.picker.Picker',
        'Ext.data.Store',
        'Ext.data.StoreManager'
    ],

    

    config: {
        
        ui: 'select',

        
        tabIndex: -1,

        

        
        valueField: 'value',

        
        displayField: 'text',

        
        store: null,

        
        options: null,

        
        hiddenName: null,

        
        component: {
            useMask: true
        },

        
        clearIcon: false
    },

    
    record: null,

    
    previousRecord: null,

    
    constructor: function(config) {
        config = config || {};

        if (!config.store) {
            config.store = true;
        }

        this.callParent([config]);
    },

    
    initialize: function() {
        this.getComponent().on({
            scope: this,
            masktap: 'onMaskTap'
        });

        this.callParent();
    },

    applyValue: function(value) {
        var record = value,
            index;
        
        if (!(value instanceof Ext.data.Model)) {
            index = this.getStore().find(this.getValueField(), value);

            if (index == -1) {
                index = this.getStore().find(this.getDisplayField(), value);
            }

            record = this.getStore().getAt(index);
        }

        return record;
    },

    updateValue: function(newValue, oldValue) {
        this.previousRecord = oldValue;
        
        if (newValue) {
            this.record = newValue;

            this.callParent([newValue.get(this.getDisplayField())]);
        }

        this.fireAction('change', [this, newValue, oldValue]);
    },

    getValue: function() {
        var record = this.record;

        return (record) ? record.get(this.getValueField()) : null;
    },

    getRecord: function() {
        return this.record;
    },

    
    getPicker: function() {
        if (!this.picker) {
            this.picker = Ext.create('Ext.picker.Picker', {
                slots: [{
                    align       : 'center',
                    name        : this.getName(),
                    valueField  : this.getValueField(),
                    displayField: this.getDisplayField(),
                    value       : this.getValue(),
                    store       : this.getStore()
                }],
                listeners: {
                    change: this.onPickerChange,
                    scope: this
                }
            });
        }

        return this.picker;
    },

    
    getListPanel: function() {
        if (!this.listPanel) {
            this.listPanel = Ext.create('Ext.Panel', {
                top     : 0,
                left    : 0,
                height  : 200,
                modal   : true,
                cls     : Ext.baseCSSPrefix + 'select-overlay',
                layout  : 'fit',
                hideOnMaskTap: true,
                items: {
                    xtype: 'list',
                    store: this.getStore(),
                    itemTpl: '<span class="x-list-label">{' + this.getDisplayField() + '}</span>',
                    listeners: {
                        select : this.onListSelect,
                        itemtap: this.onListTap,
                        scope  : this
                    }
                }
            });
        }

        return this.listPanel;
    },

    
    onMaskTap: function() {
        if (this.getDisabled()) {
            return false;
        }

        this.showComponent();

        return false;
    },

    
    showComponent: function() {
        if (Ext.os.deviceType == 'Phone') {
            var picker = this.getPicker(),
                name   = this.getName(),
                value  = {};

            value[name] = this.record.get(this.getValueField());
            picker.setValue(value);
            picker.show();
        } else {
            var listPanel = this.getListPanel(),
                list = listPanel.down('list'),
                store = list.getStore(),
                index = store.find(this.getValueField(), this.getValue()),
                record = store.getAt((index == -1) ? 0 : index);

            listPanel.showBy(this);
            list.select(record, null, true);
        }
    },

    
    onListSelect: function(item, record) {
        var me = this;
        if (record) {
            me.setValue(record);
        }
    },

    onListTap: function() {
        this.listPanel.hide({
            type : 'fade',
            out  : true,
            scope: this
        });
    },

    
    onPickerChange: function(picker, value) {
        var me = this,
            currentValue = me.getValue(),
            newValue = value[me.getName()],
            store = me.getStore(),
            index = store.find(me.getValueField(), newValue);
            record = store.getAt(index);
        
        me.setValue(record);
    },

    
    updateOptions: function(newOptions) {
        var store = this.getStore(),
            record;

        if (!newOptions) {
            store.clearData();
            this.setValue(null);
        }
        else {
            store.loadData(newOptions);

            record = store.getAt(0);
            this.setValue(record);
        }
    },

    applyStore: function(store) {
        if (store === true) {
            store = Ext.create('Ext.data.Store', {
                fields: [this.getValueField(), this.getDisplayField()]
            });
        }

        if (store) {
            store = Ext.data.StoreManager.lookup(store);
        }

        return store;
    },

    updateStore: function(newStore) {
        var record = (newStore) ? newStore.getAt(0) : null;

        if (newStore && record) {
            this.setValue(record);
        }
    },

    
    reset: function() {
        var store = this.getStore(),
            record = (this.originalValue) ? this.originalValue : store.getAt(0);

        if (store && record) {
            this.setValue(record);
        }

        return this;
    },

    destroy: function() {
        this.callParent(arguments);
        Ext.destroy(this.listPanel, this.picker, this.hiddenField);
    }
});


Ext.define('Ext.picker.Date', {
    extend: 'Ext.picker.Picker',
    xtype: 'datepicker',
    alternateClassName: 'Ext.DatePicker',
    requires: ['Ext.DateExtras'],

    config: {
        
        yearFrom: 1980,

        
        yearTo: new Date().getFullYear(),

        
        monthText: 'Month',

        
        dayText: 'Day',

        
        yearText: 'Year',

        
        slotOrder: ['month', 'day', 'year']

        
        
        

        
    },

    setValue: function(value, animated) {
        if (Ext.isDate(value)) {
            value = {
                day  : value.getDate(),
                month: value.getMonth() + 1,
                year : value.getFullYear()
            };
        }

        this.callParent([value, animated]);
    },

    getValue: function() {
        var values = {},
            daysInMonth, day,
            items = this.getItems().items,
            ln = items.length,
            item, i;

        for (i = 0; i < ln; i++) {
            item = items[i];
            if (item instanceof Ext.picker.Slot) {
                values[item.getName()] = item.getValue();
            }
        }

        daysInMonth = this.getDaysInMonth(values.month, values.year);
        day = Math.min(values.day, daysInMonth);

        return new Date(values.year, values.month - 1, day);
    },

    
    updateYearFrom: function() {
        if (this.initialized) {
            this.createSlots();
        }
    },

    
    updateYearTo: function() {
        if (this.initialized) {
            this.createSlots();
        }
    },

    
    updateMonthText: function(newMonthText, oldMonthText) {
        var innerItems = this.getInnerItems,
            ln = innerItems.length,
            item, i;
        
        
        if (this.initialized) {
            for (i = 0; i < ln; i++) {
                item = innerItems[i];

                if ((typeof item.title == "string" && item.title == oldMonthText) || (item.title.html == oldMonthText)) {
                    item.setTitle(newMonthText);
                }
            }
        }
    },

    
    updateDayText: function(newDayText, oldDayText) {
        var innerItems = this.getInnerItems,
            ln = innerItems.length,
            item, i;

        
        if (this.initialized) {
            for (i = 0; i < ln; i++) {
                item = innerItems[i];

                if ((typeof item.title == "string" && item.title == oldDayText) || (item.title.html == oldDayText)) {
                    item.setTitle(newDayText);
                }
            }
        }
    },

    
    updateYearText: function(yearText) {
        var innerItems = this.getInnerItems,
            ln = innerItems.length,
            item, i;

        
        if (this.initialized) {
            for (i = 0; i < ln; i++) {
                item = innerItems[i];

                if (item.title == this.yearText) {
                    item.setTitle(yearText);
                }
            }
        }
    },

    
    constructor: function() {
        this.callParent(arguments);
        this.createSlots();
    },

    
    createSlots: function() {
        var me        = this,
            slotOrder = this.getSlotOrder(),
            yearsFrom = me.getYearFrom(),
            yearsTo   = me.getYearTo(),
            years     = [],
            days      = [],
            months    = [],
            ln, tmp, i,
            daysInMonth;

        
        if (yearsFrom > yearsTo) {
            tmp = yearsFrom;
            yearsFrom = yearsTo;
            yearsTo = tmp;
        }

        for (i = yearsFrom; i <= yearsTo; i++) {
            years.push({
                text: i,
                value: i
            });
        }

        daysInMonth = this.getDaysInMonth(1, new Date().getFullYear());

        for (i = 0; i < daysInMonth; i++) {
            days.push({
                text: i + 1,
                value: i + 1
            });
        }

        for (i = 0, ln = Ext.Date.monthNames.length; i < ln; i++) {
            months.push({
                text: Ext.Date.monthNames[i],
                value: i + 1
            });
        }

        var slots = [];

        slotOrder.forEach(function(item) {
            slots.push(this.createSlot(item, days, months, years));
        }, this);

        me.setSlots(slots);
    },

    
    createSlot: function(name, days, months, years) {
        switch (name) {
            case 'year':
                return {
                    name: 'year',
                    align: 'center',
                    data: years,
                    title: this.getYearText(),
                    flex: 3
                };
            case 'month':
                return {
                    name: name,
                    align: 'right',
                    data: months,
                    title: this.getMonthText(),
                    flex: 4
                };
            case 'day':
                return {
                    name: 'day',
                    align: 'center',
                    data: days,
                    title: this.getDayText(),
                    flex: 2
                };
        }
    },

    
    getDaysInMonth: function(month, year) {
        var daysInMonth = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
        return month == 2 && this.isLeapYear(year) ? 29 : daysInMonth[month-1];
    },

    
    isLeapYear: function(year) {
        return !!((year & 3) === 0 && (year % 100 || (year % 400 === 0 && year)));
    }
});


Ext.define('Ext.field.DatePicker', {
    extend: 'Ext.field.Text',
    alternateClassName: 'Ext.form.DatePicker',
    alias : 'widget.datepickerfield',
    requires: [
        'Ext.picker.Date',
        'Ext.DateExtras'
    ],

    

    config: {
        ui: 'select',

        
        picker: true,

        
        clearIcon: false,

        

        
        destroyPickerOnHide: false,

        
        tabIndex: -1,

        
        component: {
            useMask: true
        }
    },

    initialize: function() {
        this.callParent(arguments);

        this.getComponent().on({
            scope: this,

            masktap: 'onMaskTap'
        });
    },

    applyValue: function(value) {
        if (!Ext.isDate(value) && !Ext.isObject(value)) {
            value = null;
        }

        if (Ext.isObject(value)) {
            value = new Date(value.year, value.month - 1, value.day);
        }

        return value;
    },

    
    updateValue: function(newValue) {
        var picker = this.getPicker();
        if (this.initialized && picker) {
            picker.setValue(newValue);
        }
        this.getComponent().setValue(Ext.Date.format(newValue, Ext.util.Format.defaultDateFormat));

        this._value = newValue;
    },

    getValue: function() {
        return this._value;
    },

    
    getFormattedValue: function(format) {
        var value = this.getValue();
        return (Ext.isDate(value)) ? Ext.Date.format(value, format || Ext.util.Format.defaultDateFormat) : value;
    },

    applyPicker: function(config) {
        if (!this.initialized) {
            
            
            
            return null;
        }

        return Ext.factory(config, Ext.picker.Date, this.getPicker());
    },

    updatePicker: function(newPicker) {
        if (newPicker) {
            newPicker.on({
                scope: this,

                change: 'onPickerChange',
                hide  : 'onPickerHide'
            });

            newPicker.hide();
        }
    },

    
    onMaskTap: function() {
        if (this.getDisabled()) {
            return false;
        }

        var picker = this.getPicker(),
            initialConfig = this.getInitialConfig();
        
        if (!picker) {
            picker = this.applyPicker(initialConfig.picker);
            this.updatePicker(picker);
            picker.setValue(initialConfig.value);
            this._picker = picker;
        }

        picker.show();

        return false;
    },

    
    onPickerChange: function(picker, value) {
        var me = this;

        me.setValue(value);
        me.fireAction('change', [me, me.getValue()], 'doChange');
    },

    doChange: Ext.emptyFn,

    
    onPickerHide: function() {
        var picker = this.getPicker();

        if (this.getDestroyPickerOnHide() && picker) {
            picker.destroy();
            this.setPicker(null);
        }
    },

    reset: function() {
        this.setValue(this.originalValue);
    },

    
    onDestroy: function() {
        var picker = this.getPicker();
        if (picker) {
            picker.destroy();
        }

        this.callParent(arguments);
    }
}, function() {
    this.override({
        getValue: function(format) {
            if (format) {
                Ext.Logger.deprecate("format argument of the getValue method is deprecated, please use getFormattedValue instead", this);
                return this.getFormattedValue(format);
            }
            return this.callOverridden();
        }
    });
});

Ext.define('Ext.slider.Thumb', {
    extend: 'Ext.Component',
    xtype : 'thumb',

    config: {
        
        baseCls: Ext.baseCSSPrefix + 'thumb',

        
        value: 0,

        
        draggable: {
            direction: 'horizontal'
        }
    },

    
    slider: null,

    
    initialize: function() {
        var me = this;

        me.callParent(arguments);

        me.on({
            scope: this,

            painted: 'onPainted'
        });
        
        
        me.getDraggable().on({
            scope: this,

            'dragstart': {
                before: 'onBeforeDragStart',
                after : 'onDragStart'
            },
            'drag'   : 'onDrag',
            'dragend': 'onDragEnd'
        });

        this.slider = this.config.slider;
    },

    
    onPainted: function() {
        
        this.refreshValue();
    },

    
    updateValue: function(newValue) {
        var slider = this.slider,
            offset, draggable;

        if (slider) {
            offset = slider.getOffsetForValue(newValue);
            draggable = this.getDraggable();

            draggable.setOffset({
                x: offset
            });
        }

        this.fireAction('change', [this, newValue]);
    },

    
    refreshValue: function() {
        this.updateValue(this.getValue());
    },

    
    onBeforeDragStart: function(draggable, e) {
        if (e.absDeltaX < 4) {
            return false;
        }
    },

    
    onDragStart: function(draggable, e, offset) {
        this.addCls(Ext.baseCSSPrefix + 'dragging');
    },

    
    onDrag: function(draggable, e, offset) {
        e.stopPropagation();

        var value = this.slider.getValueForOffset(offset.x);
        this.setValue(value);
        this.updateValue(value);
    },

    
    onDragEnd: function() {
        var me     = this,
            offset = me.getDraggable().getOffset(),
            newValue;

        newValue = me.slider.getValueForOffset(offset.x);

        me.setValue(newValue);

        me.removeCls(Ext.baseCSSPrefix + 'dragging');
    }
});


Ext.define('Ext.slider.Slider', {
    extend  : 'Ext.Container',
    alias   : 'widget.slider',
    requires: ['Ext.slider.Thumb'],

    config: {
        
        layout: null,

        
        thumbs: [],

        
        value: 0,

        
        tabIndex: -1,

        
        minValue: 0,

        
        maxValue: 100,

        
        increment: 1
    },

    
    constructor: function(config) {
        config = config || {};

        if (config.hasOwnProperty('values')) {
            config.value = config.values;
        }

        this.callParent([config]);
    },

    
    initialize: function() {
        var me = this;

        me.callParent(arguments);

        me.on({
            scope: this,
            delegate: 'thumb',

            change: 'onChange'
        });
    },

    
    refreshThumbs: function() {
        var me = this;

        
        var thumbs = this.getThumbs(),
            ln = thumbs.length,
            i;

        for (i = ln - 1; i >= 0; i--) {
            thumbs[i].refreshValue();
        }
    },

    
    applyThumbs: function(thumbs) {
        var i, ln, config, currentThumb, instance,
            currentThumbs = this.getThumbs() || [],
            instances = [];

        if (thumbs && thumbs.length > 0) {
            
            if (!Ext.isArray(thumbs)) {
                thumbs = [thumbs];
            }

            ln = thumbs.length;

            for (i = 0; i < ln; i++) {
                config = thumbs[i];
                config.slider = this;
                currentThumb = currentThumbs[i];

                instance = Ext.factory(config, 'Ext.slider.Thumb', currentThumb);
                instances.push(instance);
            }

            return instances;
        }

        return thumbs;
    },

    
    updateThumbs: function(newThumbs) {
        if (newThumbs) {
            this.add(newThumbs);
        }
    },

    
    applyValue: function(value) {
        
        this.getValue();

        
        if (!Ext.isArray(value)) {
            value = [value];
        }

        return value;
    },

    
    updateValue: function(newValue) {
        var thumbs = this.getThumbs(),
            newThumbs = [],
            ln = newValue.length,
            thumb, i;

        
        if (thumbs.length === 0) {
            for (i = 0; i < ln; i++) {
                newThumbs.push({
                    value: newValue[i]
                });
            }

            this.setThumbs(newThumbs);

            return;
        }

        
        ln = newValue.length;
        for (i = ln - 1; i >= 0; i--) {
            thumb = thumbs[i];
            if (thumb) {
                thumbs[i].setValue(newValue[i]);
            }
            else {
                throw new Error("Ext.slider.Slider: [setValue] calling setValue() with more values than there are thumbs (" + thumbs.length + " thumb(s), " + ln +" value(s)).");
            }
        }

        this._value = newValue;
    },

    
    getValue: function() {
        var thumbs = this.getThumbs(),
            ln = thumbs.length,
            value = [],
            i;
        
        
        for (i = 0; i < ln; i++) {
            value.push(thumbs[i].getValue());
        }

        this._value = value;

        return value;
    },

    
    setValues: function(value) {
        this.updateValue(this.applyValue(value));
        this._value = value;
    },

    
    
    
    

    
    getValues: function() {
        return this.getValue();
    },

    
    applyIncrement: function(increment) {
        if (increment === 0) {
            increment = 1;
        }

        increment = Math.abs(increment);

        return increment;
    },

    
    updateMinValue: function(newMinValue) {
        this.refreshThumbs();
    },

    
    updateMaxValue: function(newMaxValue) {
        this.refreshThumbs();
    },

    
    updateIncrement: function(newIncrement) {
        this.refreshThumbs();
    },

    
    getThumb: function(index) {
        var thumbs = this.getThumbs();
        return thumbs[index || 0];
    },

    
    getClosestThumb: function(value) {
        var thumbs = this.getThumbs(),
            ln     = thumbs.length,
            thumb  = thumbs[0],
            difference = Infinity,
            thumbDifference, thumbValue, i;

        if (ln == 1) {
           return thumb;
        }

        
        for (i = 0; i < ln; i++) {
            thumbValue = thumbs[i].getValue();
            thumbDifference = Math.abs(thumbValue - value);
            if (thumbDifference < difference) {
                difference = thumbDifference;
                thumb = thumbs[i];
            }
        }

        return thumb;
    },

    
    indexOf: function(thumb) {
        return this.getThumbs().indexOf(thumb);
    },

    
    getOffsetForValue: function(value) {
        var me = this,
            minValue   = me.getMinValue(),
            maxValue   = me.getMaxValue(),
            range      = maxValue - minValue,
            trackWidth = me.innerElement.getWidth(),
            thumbWidth = 0,
            thumb, ratio;

        thumb = me.getThumb();
        if (thumb) {
            thumbWidth = thumb.renderElement.getWidth();
        }
        trackWidth = trackWidth - thumbWidth;

        value = this.constrain(value);
        ratio = trackWidth / range;

        return Math.round((ratio * (value - minValue)));
    },

    
    getValueForOffset: function(offset, isTap) {
        var me = this,
            minValue   = me.getMinValue(),
            maxValue   = me.getMaxValue(),
            range      = maxValue - minValue,
            trackWidth = me.innerElement.getWidth(),
            thumbWidth = 0,
            thumb, ratio;

        thumb = me.getThumb();
        if (thumb) {
            thumbWidth = thumb.renderElement.getWidth();
        }
        trackWidth = trackWidth - ((isTap) ? 0 : thumbWidth);

        ratio = range / trackWidth;

        return Math.round(minValue + (ratio * (offset)));
    },

    
    constrain: function(value) {
        var me = this,
            minValue  = me.getMinValue(),
            maxValue  = me.getMaxValue(),
            increment = me.getIncrement(),
            remainder = value % increment;

        value -= remainder;

        if (Math.abs(remainder) >= (increment / 2)) {
            value += (remainder > 0) ? increment : -increment;
        }

        value = Math.max(minValue, value);
        value = Math.min(maxValue, value);

        return value;
    },

    
    setThumbsDisabled: function(disable) {
        var me = this,
            thumbs = me.getThumbs(),
            ln     = thumbs.length,
            i;

        for (i = 0; i < ln; i++) {
            thumbs[i][disable ? 'disable' : 'enable']();
        }
    },

    
    onChange: function(thumb, value) {
        var thumbs = this.getThumbs(),
            ln = thumbs.length,
            thumbWidth = thumb.renderElement.getWidth(),
            previousThumb, offset, previousOffset, i, thumbDraggable, previousThumbDraggable;

        for (i = 0; i < ln; i++) {
            thumb         = thumbs[i];
            previousThumb = thumbs[i - 1];
            thumbDraggable         = (thumb) ? thumb.getDraggable() : null;
            previousThumbDraggable = (previousThumb) ? previousThumb.getDraggable() : null;

            if (previousThumb && thumbDraggable && previousThumbDraggable) {
                offset = thumbDraggable.getOffset().x;
                previousOffset = previousThumb.getDraggable().getOffset().x;

                thumbDraggable.setConstraint({
                    min: {
                        x: (previousOffset === 0) ? thumbWidth : previousOffset + thumbWidth
                    }
                });

                previousThumbDraggable.setConstraint({
                    max: {
                        x: offset - thumbWidth
                    }
                });
            }
        }

        this.fireEvent('change', this, thumb, value);
    },

    doSetDisabled: function(disabled) {
        this.callParent(arguments);
        this.setThumbsDisabled(disabled);
    },

    
    reset: function() {
        this.setValues(this.originalValue);
    }
});


Ext.define('Ext.field.Slider', {
    extend  : 'Ext.field.Field',
    alias   : 'widget.sliderfield',
    requires: ['Ext.slider.Slider'],
    alternateClassName: 'Ext.form.Slider',

    

    config: {
        
        cls: Ext.baseCSSPrefix + 'slider',

        
        thumbs: [],

        
        value: 0,

        
        tabIndex: -1,

        
        minValue: 0,

        
        maxValue: 100,

        
        increment: 1,

        component: {
            xtype: 'slider'
        }
    },

    
    constructor: function(config) {
        config = config || {};

        if (config.hasOwnProperty('values')) {
            config.value = config.values;
        }

        this.callParent([config]);
    },

    
    initialize: function() {
        var me = this;

        me.callParent(arguments);

        me.element.on({
            scope: this,
            tap: 'onTap'
        });

        me.on({
            scope: this,
            painted: 'onPainted',
            show: 'onPainted'
        });

        me.getComponent().on({
            scope: this,
            change: 'onChange'
        });
        
        this.sizeMonitor = Ext.create('Ext.util.SizeMonitor', {
            element : this.element,
            callback: this.onSizeChange,
            scope   : this
        });
    },

    onPainted: function() {
        this.sizeMonitor.refresh();
        this.onSizeChange();
    },

    onSizeChange: function() {
        this.getComponent().refreshThumbs();
    },

    
    applyComponent: function(config) {
        Ext.applyIf(config, {
            value: this.getValue()
        });

        return Ext.factory(config);
    },

    onChange: function(me, thumb, value) {
        this.fireEvent('change', this, thumb, value);
    },

    
    onTap: function(e) {
        var el = Ext.get(e.target);

        if (!el || el.hasCls(Ext.baseCSSPrefix + 'thumb')) {
            return;
        }

        var component = this.getComponent(),
            touchX = e.touch.point.x,
            parent = component.element,
            parentX = parent.getX(),
            offset = touchX - parentX,
            value  = component.getValueForOffset(offset, true),
            thumb  = component.getClosestThumb(value);

        thumb.setValue(value);
    },

    
    updateMinValue: function(value) {
        this.getComponent().setMinValue(value);
    },

    
    updateMaxValue: function(value) {
        this.getComponent().setMaxValue(value);
    },

    
    updateIncrement: function(value) {
        this.getComponent().setIncrement(value);
    },

    
    getValue: function() {
        var value;

        if (this.initialized) {
            value = this.getComponent().getValue();
        } else {
            value = this.getInitialConfig().value;
        }

        this._value = value;

        return value;
    },

    
    getValues: function() {
        return this.getValue();
    },

    
    setValues: function(value) {
        return this.setValue(value);
    },

    updateValue: function(value) {
        this.getComponent().setValue(value);
    },

    
    applyIncrement: function(increment) {
        if (increment === 0) {
            increment = 1;
        }

        increment = Math.abs(increment);

        return increment;
    },

    
    getThumb: function(index) {
        return this.getComponent().getThumb(index);
    },

    
    getClosestThumb: function(value) {
        return this.getClosestThumb(value);
    },

    
    indexOf: function(thumb) {
        return this.getComponent().indexOf(thumb);
    },

    
    disable: function() {
        this.callParent();
        this.getComponent().disable();
    },

    
    enable: function() {
        this.callParent();
        this.getComponent().enable();
    },

    
    reset: function() {
        var component = this.getComponent();

        component.originalValue = this.originalValue;
        component.reset();
        
        this.getValues();
    }
});


Ext.define('Ext.field.Toggle', {
    extend: 'Ext.field.Slider',
    alias : 'widget.togglefield',
    alternateClassName: 'Ext.form.Toggle',

    config: {
        
        cls: 'x-toggle',

        
        minValue: 0,

        
        maxValue: 1,

        
        minValueCls: Ext.baseCSSPrefix + 'toggle-off',

        
        maxValueCls: Ext.baseCSSPrefix + 'toggle-on'

        
        
    },

    
    onChange: function(slider, thumb, newValue) {
        var me = this,
            innerElement = me.innerElement,
            isOn = newValue > 0,
            onCls = me.getMaxValueCls(),
            offCls = me.getMinValueCls();

        innerElement.addCls(isOn ? onCls : offCls);
        innerElement.removeCls(isOn ? offCls : onCls);

        this.fireEvent('change', this, thumb, newValue);
    },

    onTap: function(e) {
        var value = (this.getValue() > 0) ? 0 : 1;
        this.setValue(value);
    },

    getValue: function() {
        var value;

        if (this.initialized) {
            value = this.getComponent().getValue();
        } else {
            value = this.getInitialConfig().value;
        }

        if (Ext.isArray(value)) {
            value = value[0];
        }

        this._value = value;
        this._values = value;

        return value;
    }
});

Ext.define('Ext.tab.Tab', {
    extend: 'Ext.Button',
    xtype: 'tab',
    alternateClassName: 'Ext.Tab',

    
    isTab: true,

    config: {
        
        baseCls: Ext.baseCSSPrefix + 'tab',

        
        pressedCls: Ext.baseCSSPrefix + 'tab-pressed',

        
        activeCls: Ext.baseCSSPrefix + 'tab-active',

        
        active: false,

        
        title: '&nbsp;'
    },

    

    

     
    updateTitle: function(title) {
        this.setText(title);
    },

    
    updateActive: function(active) {
        var activeCls = this.getActiveCls();
        if (active) {
            this.addCls(activeCls);
            this.fireEvent('activate', this);
        } else {
            this.removeCls(activeCls);
            this.fireEvent('deactivate', this);
        }
    }
}, function() {
    this.override({
        activate: function() {
            this.setActive(true);
        },

        deactivate: function() {
            this.setActive(false);
        }
    });
});


Ext.define('Ext.tab.Bar', {
    extend: 'Ext.Toolbar',
    alternateClassName: 'Ext.TabBar',
    xtype : 'tabbar',

    
    requires: ['Ext.tab.Tab'],

    config: {
        
        activeTab: null,

        
        baseCls: Ext.baseCSSPrefix + 'tabbar',

        
        defaultType: 'tab',

        
        layout: {
            type : 'hbox',
            align: 'middle',
            pack : 'left'
        }
    },

    

    initialize: function() {
        var me = this;

        me.on({
            tap: 'onTabTap',

            delegate: '> tab',
            scope   : me
        });

        me.callParent(arguments);
    },

    
    onTabTap: function(tab) {
        this.setActiveTab(tab);
    },

    
    applyActiveTab: function(activeTab) {
        if (!activeTab && activeTab !== 0) {
            return;
        }

        var activeTabInstance = this.parseActiveTab(activeTab);
        if (!activeTabInstance) {
            Ext.Logger.warn('Trying to set a non-existent activeTab');
            return;
        }
        return activeTabInstance;
    },
    
    
    doSetDocked: function(newDocked) {
        var layout = this.getLayout(),
            pack   = newDocked == 'bottom' ? 'center' : 'left';
        
        
        if (layout.isLayout) {
            layout.setPack(pack);
        } else {
            layout.pack = pack;
        }
    },

    
    updateActiveTab: function(newTab, oldTab) {
        this.fireAction('tabchange', [this, newTab, oldTab], 'doActiveTabChange');
    },

    
    doActiveTabChange: function(me, newTab, oldTab) {
        if (newTab) {
            newTab.setActive(true);
        }

        if (oldTab) {
            oldTab.setActive(false);
        }
    },

    
    parseActiveTab: function(tab) {
        
        var items = this.getItems();

        if (typeof tab == 'number') {
            return this.getInnerItems()[tab];
        } else if (typeof tab == 'string') {
            tab = Ext.getCmp(tab);
        }
        return tab;
    }
});


Ext.define('Ext.tab.Panel', {
    extend: 'Ext.Container',
    xtype : ['tabpanel'],
    alternateClassName: 'Ext.TabPanel',

    requires: ['Ext.tab.Bar'],

    
    config: {
        
        ui: 'dark',

        
        tabBar: {
            docked: 'top'
        },

        
        tabBarPosition: null,

        
        layout: {
            type: 'card',
            animation: {
                type: 'slide',
                direction: 'left'
            }
        },

        
        cls: Ext.baseCSSPrefix + 'tabpanel'
    },
    
    initialize: function() {
        this.on({
            tabchange: 'doTabChange',
            delegate: '> tabbar',
            scope   : this
        });
    },

    
    updateUi: function(newUi, oldUi) {
        this.callParent(arguments);

        if (this.initialized) {
            this.getTabBar().setUi(newUi);
        }
    },

    
    doActiveItemChange: function(newCard) {
        this.callParent(arguments);
        this.getTabBar().setActiveTab(this.getInnerItems().indexOf(newCard));
    },

    doSetActiveItem: function(activeItem) {
        var items = this.getInnerItems(),
            currentIndex = items.indexOf(this.getActiveItem()),
            index = Ext.isNumber(activeItem) ? activeItem : items.indexOf(activeItem),
            reverse = currentIndex > index;

        this.getLayout().getAnimation().setReverse(reverse);

        this.callParent(arguments);

        if (index != -1) {
            this.getTabBar().setActiveTab(index);
        }
    },

    
    doTabChange: function(tabBar, newTab, oldTab) {
        var index = tabBar.indexOf(newTab);
        this.setActiveItem(index);
    },

    
    applyTabBar: function(config) {
        return Ext.factory(config, Ext.tab.Bar, this.getTabBar());
    },

    
    updateTabBar: function(newTabBar) {
        if (newTabBar) {
            newTabBar.setUi(this.getUi());
            this.add(newTabBar);
            this._tabBarPosition = newTabBar.getDocked();
        }
    },

    
    updateTabBarPosition: function(position) {
        this.getTabBar().setDocked(position);
    },

    
    onAdd: function(card) {
        var me = this;

        if (!card.isInnerItem()) {
            return me.callParent(arguments);
        }

        var tabBar             = me.getTabBar(),
            initialConfig      = card.getInitialConfig(),
            tabConfig          = initialConfig.tab || {},
            tabTitle           = initialConfig.title,
            tabIconCls         = initialConfig.iconCls,
            tabBadgeText       = initialConfig.badgeText,
            innerItems         = me.getInnerItems(),
            index              = innerItems.indexOf(card),
            tabs               = tabBar.getItems(),
            cards              = me.getInnerItems(),
            currentTabInstance = (tabs.length >= cards.length) && tabs.getAt(index),
            tabInstance;

        if (tabTitle && !tabConfig.title) {
            tabConfig.title = tabTitle;
        }

        if (tabIconCls && !tabConfig.iconCls) {
            tabConfig.iconCls = tabIconCls;
        }

        if (tabBadgeText && !tabConfig.badgeText) {
            tabConfig.badgeText = tabBadgeText;
        }

        if (!currentTabInstance && !tabConfig.title && !tabConfig.iconCls) {
            if (!tabConfig.title && !tabConfig.iconCls) {
                Ext.Logger.error('Adding a card to a tab container without specifying any tab configuration');
            }
        }

        tabInstance = Ext.factory(tabConfig, Ext.tab.Tab, currentTabInstance);

        if (!currentTabInstance) {
            tabBar.insert(index, tabInstance);
        }

        me.callParent(arguments);
    }



































    







});


Ext.define('Ext.viewport.Default', {
    extend: 'Ext.Container',

    xtype: 'viewport',

    PORTRAIT: 'portrait',

    LANDSCAPE: 'landscape',

    config: {
        
        autoMaximize: Ext.browser.is.WebView ? false : true,

        
        preventPanning: true,

        
        preventZooming: true,

        autoRender: true,

        layout: 'card',

        width: '100%',

        height: '100%'
    },

    isReady: false,

    isViewport: true,

    isMaximizing: false,

    id: 'ext-viewport',

    isInputRegex: /^(input|textarea|select)$/i,

    focusedElement: null,

    
    fullscreenItemCls: Ext.baseCSSPrefix + 'fullscreen',

    constructor: function(config) {
        var bind = Ext.Function.bind;

        this.doPreventPanning = bind(this.doPreventPanning, this);
        this.doPreventZooming = bind(this.doPreventZooming, this);

        this.maximizeOnEvents = ['ready', 'orientationchange'];

        this.orientation = this.determineOrientation();
        this.windowWidth = this.getWindowWidth();
        this.windowHeight = this.getWindowHeight();
        this.windowOuterHeight = this.getWindowOuterHeight();

        if (!this.stretchHeights) {
            this.stretchHeights = {};
        }

        this.callParent([config]);

        if (this.supportsOrientation()) {
            this.addWindowListener('orientationchange', bind(this.onOrientationChange, this));
        }
        else {
            this.addWindowListener('resize', bind(this.onResize, this));
        }

        document.addEventListener('focus', bind(this.onElementFocus, this), true);
        document.addEventListener('blur', bind(this.onElementBlur, this), true);

        Ext.onDocumentReady(this.onDomReady, this);

        this.on('ready', this.onReady, this, {single: true});

        this.getEventDispatcher().addListener('component', '*', 'fullscreen', 'onItemFullscreenChange', this);

        return this;
    },

    onDomReady: function() {
        this.isReady = true;
        this.fireEvent('ready');
    },

    onReady: function() {
        if (this.getAutoRender()) {
            this.render();
        }
    },

    onElementFocus: function(e) {
        this.focusedElement = e.target;
    },

    onElementBlur: function() {
        this.focusedElement = null;
    },

    render: function() {
        if (!this.rendered) {
            var body = Ext.getBody(),
                clsPrefix = Ext.baseCSSPrefix,
                classList = [],
                osEnv = Ext.os,
                osName = osEnv.name.toLowerCase(),
                osMajorVersion = osEnv.version.getMajor(),
                orientation = this.getOrientation();

            this.renderTo(body);

            
            classList.push(clsPrefix + osEnv.deviceType.toLowerCase());

            if (osEnv.is.iPad) {
                classList.push(clsPrefix + 'ipad');
            }

            classList.push(clsPrefix + osName);

            if (osMajorVersion) {
                classList.push(clsPrefix + osName + '-' + osMajorVersion);
            }

            if (osEnv.is.BlackBerry) {
                classList.push(clsPrefix + 'bb');
            }

            if (Ext.browser.is.Standalone) {
                classList.push(clsPrefix + 'standalone');
            }

            classList.push(clsPrefix + orientation);

            body.addCls(classList);
        }
    },

    applyAutoMaximize: function(autoMaximize) {
        if (autoMaximize) {
            this.on('ready', 'doAutoMaximizeOnReady', this, { single: true });
            this.on('orientationchange', 'doAutoMaximizeOnOrientationChange', this);
        }
        else {
            this.un('ready', 'doAutoMaximizeOnReady', this);
            this.un('orientationchange', 'doAutoMaximizeOnOrientationChange', this);
        }

        return autoMaximize;
    },

    applyPreventPanning: function(preventPanning) {
        if (preventPanning) {
            this.addWindowListener('touchmove', this.doPreventPanning, false);
        }
        else {
            this.removeWindowListener('touchmove', this.doPreventPanning, false);
        }

        return preventPanning;
    },

    applyPreventZooming: function(preventZooming) {
        if (preventZooming) {
            this.addWindowListener('touchstart', this.doPreventZooming, false);
        }
        else {
            this.removeWindowListener('touchstart', this.doPreventZooming, false);
        }

        return preventZooming;
    },

    doAutoMaximizeOnReady: function() {
        var controller = arguments[arguments.length - 1];

        controller.pause();

        this.isMaximizing = true;

        this.on('maximize', function() {
            this.isMaximizing = false;

            this.updateSize();

            controller.resume();

            this.fireEvent('ready');
        }, this, { single: true });

        this.maximize();
    },

    doAutoMaximizeOnOrientationChange: function() {
        var controller = arguments[arguments.length - 1],
            firingArguments = controller.firingArguments;

        controller.pause();

        this.isMaximizing = true;

        this.on('maximize', function() {
            this.isMaximizing = false;

            this.updateSize();

            firingArguments[1] = this.windowWidth;
            firingArguments[2] = this.windowHeight;

            controller.resume();
        }, this, { single: true });

        this.maximize();
    },

    doPreventPanning: function(e) {
        e.preventDefault();
    },

    doPreventZooming: function(e) {
        var target = e.target;

        if (target && target.nodeType === 1 && !this.isInputRegex.test(target.tagName)) {
            e.preventDefault();
        }
    },

    addWindowListener: function(eventName, fn, capturing) {
        window.addEventListener(eventName, fn, capturing);
    },

    removeWindowListener: function(eventName, fn, capturing) {
        window.removeEventListener(eventName, fn, capturing);
    },

    doAddListener: function(eventName, fn, scope, options) {
        if (eventName === 'ready' && this.isReady && !this.isMaximizing) {
            fn.call(scope);
            return this;
        }

        this.mixins.observable.doAddListener.apply(this, arguments);
    },

    supportsOrientation: function() {
        return Ext.feature.has.Orientation;
    },

    onResize: function() {
        var oldWidth = this.windowWidth,
            oldHeight = this.windowHeight,
            width = this.getWindowWidth(),
            height = this.getWindowHeight(),
            currentOrientation = this.getOrientation(),
            newOrientation = this.determineOrientation();

        if (oldWidth !== width || oldHeight !== height) {
            this.fireResizeEvent(width, height);

            if (currentOrientation !== newOrientation) {
                this.fireOrientationChangeEvent(newOrientation, currentOrientation);
            }
        }
    },

    fireResizeEvent: function(width, height) {
        this.updateSize(width, height);
        this.fireEvent('resize', width, height);
    },

    onOrientationChange: function() {
        var currentOrientation = this.getOrientation(),
            newOrientation = this.determineOrientation();

        if (newOrientation !== currentOrientation) {
            this.fireOrientationChangeEvent(newOrientation, currentOrientation);
            this.fireResizeEvent(this.windowWidth, this.windowHeight);
        }
    },

    fireOrientationChangeEvent: function(newOrientation, oldOrientation) {
        var clsPrefix = Ext.baseCSSPrefix;
        Ext.getBody().replaceCls(clsPrefix + oldOrientation, clsPrefix + newOrientation);

        this.orientation = newOrientation;

        this.updateSize();
        this.fireEvent('orientationchange', newOrientation, this.windowWidth, this.windowHeight);
    },

    updateSize: function(width, height) {
        this.windowWidth = width !== undefined ? width : this.getWindowWidth();
        this.windowHeight = height !== undefined ? height : this.getWindowHeight();

        return this;
    },

    waitUntil: function(condition, onSatisfied, onTimeout, delay, timeoutDuration) {
        if (!delay) {
            delay = 50;
        }

        if (!timeoutDuration) {
            timeoutDuration = 2000;
        }

        var scope = this,
            elapse = 0;

        setTimeout(function repeat() {
            elapse += delay;

            if (condition.call(scope) === true) {
                if (onSatisfied) {
                    onSatisfied.call(scope);
                }
            }
            else {
                if (elapse >= timeoutDuration) {
                    if (onTimeout) {
                        onTimeout.call(scope);
                    }
                }
                else {
                    setTimeout(repeat, delay);
                }
            }
        }, delay);
    },

    maximize: function() {
        this.fireMaximizeEvent();
    },

    fireMaximizeEvent: function() {
        this.updateSize();
        this.fireEvent('maximize');
    },

    doSetHeight: function(height) {
        Ext.getBody().setHeight(height);

        this.callParent(arguments);
    },

    doSetWidth: function(width) {
        Ext.getBody().setWidth(width);

        this.callParent(arguments);
    },

    scrollToTop: function() {
        window.scrollTo(0, -1);
    },

    getWindowWidth: function() {
        return window.innerWidth;
    },

    getWindowHeight: function() {
        return window.innerHeight;
    },

    getWindowOuterHeight: function() {
        return window.outerHeight;
    },

    getWindowOrientation: function() {
        return window.orientation;
    },

    getOrientation: function() {
        return this.orientation;
    },

    getSize: function() {
        return {
            width: this.windowWidth,
            height: this.windowHeight
        };
    },

    determineOrientation: function() {
        var portrait = this.PORTRAIT,
            landscape = this.LANDSCAPE;

        if (this.supportsOrientation()) {
            if (this.getWindowOrientation() % 180 === 0) {
                return portrait;
            }

            return landscape;
        }
        else {
            if (this.getWindowHeight() >= this.getWindowWidth()) {
                return portrait;
            }

            return landscape;
        }
    },

    onItemFullscreenChange: function(item) {
        item.addCls(this.fullscreenItemCls);
        this.add(item);
    }
});

Ext.define('Ext.viewport.Android', {
    extend: 'Ext.viewport.Default',

    constructor: function() {
        this.on('orientationchange', 'doFireOrientationChangeEvent', this, { prepend: true });

        return this.callParent(arguments);
    },

    doFireOrientationChangeEvent: function() {
        var eventController = arguments[arguments.length - 1];

        this.orientationChanging = true;

        eventController.pause();

        this.waitUntil(function() {
            return this.getWindowOuterHeight() !== this.windowOuterHeight;
        }, function() {
            this.windowOuterHeight = this.getWindowOuterHeight();
            this.updateSize();

            eventController.firingArguments[1] = this.windowWidth;
            eventController.firingArguments[2] = this.windowHeight;
            eventController.resume();
            this.orientationChanging = false;

        }, function() {
            Ext.Logger.error("Timeout waiting for viewport's outerHeight to change before firing orientationchange", this);
        });

        return this;
    },

    maximize: function() {
        var stretchHeights = this.stretchHeights,
            orientation = this.orientation,
            height;

        height = stretchHeights[orientation];

        if (!height) {
            stretchHeights[orientation] = height = Math.round(this.getWindowOuterHeight() / window.devicePixelRatio);
        }

        if (!this.addressBarHeight) {
            this.addressBarHeight = height - this.getWindowHeight();
        }

        this.setHeight(height);

        var isHeightMaximized = Ext.Function.bind(this.isHeightMaximized, this, [height]);

        this.scrollToTop();
        this.waitUntil(isHeightMaximized, this.fireMaximizeEvent, this.fireMaximizeEvent);
    },

    isHeightMaximized: function(height) {
        this.scrollToTop();
        return this.getWindowHeight() === height;
    }

}, function() {
    if (!Ext.os.is.Android) {
        return;
    }

    var version = Ext.os.version,
        userAgent = Ext.browser.userAgent,
        
        
        isBuggy = /(htc|desire|incredible|ADR6300)/i.test(userAgent) && version.lt('2.3');

    if (isBuggy) {
        this.override({
            constructor: function(config) {
                if (!config) {
                    config = {};
                }

                config.autoMaximize = false;

                this.watchDogTick = Ext.Function.bind(this.watchDogTick, this);

                setInterval(this.watchDogTick, 1000);

                return this.callParent([config]);
            },

            watchDogTick: function() {
                this.watchDogLastTick = Ext.Date.now();
            },

            doPreventPanning: function() {
                var now = Ext.Date.now(),
                    lastTick = this.watchDogLastTick,
                    deltaTime = now - lastTick;

                
                if (deltaTime >= 2000) {
                    return;
                }

                return this.callParent(arguments);
            },

            doPreventZooming: function() {
                var now = Ext.Date.now(),
                    lastTick = this.watchDogLastTick,
                    deltaTime = now - lastTick;

                
                if (deltaTime >= 2000) {
                    return;
                }

                return this.callParent(arguments);
            }
        });
    }

    if (version.match('2')) {
        this.override({
            onReady: function() {
                this.addWindowListener('resize', Ext.Function.bind(this.onWindowResize, this));

                this.callParent(arguments);
            },

            scrollToTop: function() {
                document.body.scrollTop = 100;
            },

            onWindowResize: function() {
                var oldWidth = this.windowWidth,
                    oldHeight = this.windowHeight,
                    width = this.getWindowWidth(),
                    height = this.getWindowHeight();

                if (this.getAutoMaximize() && !this.isMaximizing && !this.orientationChanging
                    && window.scrollY === 0
                    && oldWidth === width
                    && height < oldHeight
                    && ((height >= oldHeight - this.addressBarHeight) || !this.focusedElement)) {
                        this.scrollToTop();
                }
            }
        });
    }
    else if (version.gtEq('3.1')) {
        this.override({
            isHeightMaximized: function(height) {
                this.scrollToTop();
                return this.getWindowHeight() === height - 1;
            }
        });
    }
    else if (version.match('3')) {
        this.override({
            isHeightMaximized: function() {
                this.scrollToTop();
                return true;
            }
        })
    }
});

Ext.define('Ext.viewport.Ios', {
    extend: 'Ext.viewport.Default',

    isFullscreen: function() {
        return this.isHomeScreen();
    },

    isHomeScreen: function() {
        return window.navigator.standalone === true;
    },

    constructor: function() {
        this.callParent(arguments);

        this.addWindowListener('touchstart', Ext.Function.bind(this.onTouchStart, this));
    },

    maximize: function() {
        if (this.isFullscreen()) {
            return this.callParent();
        }

        var stretchHeights = this.stretchHeights,
            orientation = this.orientation,
            currentHeight = this.getWindowHeight(),
            height = stretchHeights[orientation];

        if (window.scrollY > 0) {
            this.scrollToTop();

            if (!height) {
                stretchHeights[orientation] = height = this.getWindowHeight();
            }

            this.setHeight(height);
            this.fireMaximizeEvent();
        }
        else {
            if (!height) {
                height = this.getScreenHeight();
            }

            this.setHeight(height);

            this.waitUntil(function() {
                this.scrollToTop();
                return currentHeight !== this.getWindowHeight();
            }, function() {
                if (!stretchHeights[orientation]) {
                    height = stretchHeights[orientation] = this.getWindowHeight();
                    this.setHeight(height);
                }

                this.fireMaximizeEvent();
            }, function() {
                Ext.Logger.error("Timeout waiting for window.innerHeight to change", this);
            });
        }
    },

    getScreenHeight: function() {
        return window.screen[this.orientation === this.PORTRAIT ? 'height' : 'width'];
    },

    onElementFocus: function() {
        clearTimeout(this.scrollToTopTimer);

        this.callParent(arguments);
    },

    onElementBlur: function() {
        this.scrollToTopTimer = setTimeout(this.scrollToTop, 500);

        this.callParent(arguments);
    },

    onTouchStart: function() {
        if (this.focusedElement === null) {
            this.scrollToTop();
        }
    },

    scrollToTop: function() {
        window.scrollTo(0, 0);
    }

}, function() {
    if (!Ext.os.is.iOS) {
        return;
    }

    if (Ext.os.version.lt('3.2')) {
        this.override({
            constructor: function() {
                var stretchHeights = this.stretchHeights = {};

                stretchHeights[this.PORTRAIT] = 416;
                stretchHeights[this.LANDSCAPE] = 268;

                return this.callOverridden(arguments);
            }
        });
    }

    if (Ext.os.version.lt('5')) {
        this.override({
            fieldMaskClsTest: '-field-mask',

            doPreventZooming: function(e) {
                var target = e.target;

                if (target && target.nodeType === 1 &&
                    !this.isInputRegex.test(target.tagName) &&
                    target.className.indexOf(this.fieldMaskClsTest) == -1) {
                    e.preventDefault();
                }
            }
        });
    }

    if (Ext.os.is.iPad) {
        this.override({
            isFullscreen: function() {
                return true;
            }
        });
    }
});


Ext.define('Ext.viewport.Viewport', {
    requires: [
        'Ext.viewport.Ios',
        'Ext.viewport.Android'
    ],

    constructor: function(config) {
        var osName = Ext.os.name,
            viewportName, viewport;

        switch (osName) {
            case 'Android':
                viewportName = 'Android';
                break;

            case 'iOS':
                viewportName = 'Ios';
                break;

            default:
                viewportName = 'Default';
        }

        viewport = Ext.create('Ext.viewport.' + viewportName, config);

        return viewport;
    }
});




